Skip to main content

cast_sender/namespace/
media.rs

1use crate::{Image, Payload, Volume};
2
3// https://developers.google.com/cast/docs/reference/web_receiver/cast.framework.messages
4// https://developers.google.com/cast/docs/reference/web_receiver/index-all
5
6#[derive(Serialize, Deserialize, Clone, Debug)]
7#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
8#[serde(tag = "type")]
9pub enum Media {
10    // Request
11    GetStatus(GetStatusRequestData),
12    Load(LoadRequestData),
13    Pause(RequestData),
14    Stop(RequestData),
15    Play(RequestData),
16    SkipAd(RequestData),
17    PlayAgain(RequestData),
18    Seek(SeekRequestData),
19    SetPlaybackRate(SetPlaybackRateRequestData),
20    SetVolume(VolumeRequestData),
21    EditTracksInfo(EditTracksInfoRequestData),
22    EditAudioTracks(EditAudioTracksRequestData),
23    Precache(PrecacheRequestData),
24    Preload(PreloadRequestData),
25    QueueLoad(QueueLoadRequestData),
26    QueueInsert(QueueInsertRequestData),
27    QueueUpdate(QueueUpdateRequestData),
28    QueueRemove(QueueRemoveRequestData),
29    QueueReorder(QueueReorderRequestData),
30    QueueNext(RequestData),
31    QueuePrev(RequestData),
32    QueueGetItemRange(FetchItemsRequestData),
33    QueueGetItems(GetItemsInfoRequestData),
34    QueueGetItemIds(RequestData),
35    QueueShuffle(RequestData),
36    SetCredentials(SetCredentialsRequestData),
37    LoadByEntity(LoadByEntityRequestData),
38    UserAction(UserActionRequestData),
39    DisplayStatus(DisplayStatusRequestData),
40    FocusState(FocusStateRequestData),
41    StoreSession(StoreSessionRequestData),
42    ResumeSession(ResumeSessionRequestData),
43
44    // Response
45    MediaStatus(ResponseData<MediaStatus>),
46    CloudStatus(ResponseData<CloudMediaStatus>),
47    QueueChange(ResponseData<QueueChange>),
48    QueueItems(ResponseData<ItemsInfo>),
49    QueueItemIds(ResponseData<QueueIds>),
50    SessionState(StoreSessionResponseData),
51
52    InvalidRequest(ErrorResponseData),
53    InvalidPlayerState,
54    LoadFailed,
55    LoadCancelled,
56}
57
58impl From<Media> for Payload {
59    fn from(val: Media) -> Self {
60        Payload::Media(val.clone())
61    }
62}
63
64// REQUEST DATA -------------------------------------------------
65
66#[skip_serializing_none]
67#[derive(Serialize, Deserialize, Clone, Debug, Default, Builder)]
68#[serde(rename_all = "camelCase")]
69#[builder(setter(strip_option, into), default)]
70pub struct DisplayStatusRequestData {}
71
72#[skip_serializing_none]
73#[derive(Serialize, Deserialize, Clone, Debug, Default, Builder)]
74#[serde(rename_all = "camelCase")]
75#[builder(setter(strip_option, into), default)]
76pub struct EditAudioTracksRequestData {
77    pub is_suggested_language: Option<bool>,
78    pub language: Option<String>,
79    pub media_session_id: Option<i32>,
80}
81
82#[skip_serializing_none]
83#[derive(Serialize, Deserialize, Clone, Debug, Default, Builder)]
84#[serde(rename_all = "camelCase")]
85#[builder(setter(strip_option, into), default)]
86pub struct EditTracksInfoRequestData {
87    pub active_track_ids: Option<Vec<i32>>,
88    pub enable_text_tracks: Option<bool>,
89    pub is_suggested_language: Option<bool>,
90    pub language: Option<String>,
91    pub media_session_id: Option<i32>,
92    pub text_track_style: Option<TextTrackStyle>,
93}
94
95#[skip_serializing_none]
96#[derive(Serialize, Deserialize, Clone, Debug, Default, Builder)]
97#[serde(rename_all = "camelCase")]
98#[builder(setter(strip_option, into), default)]
99pub struct FetchItemsRequestData {
100    pub item_id: i32,
101    pub media_session_id: Option<i32>,
102    pub next_count: i32,
103    pub prev_count: i32,
104}
105
106#[skip_serializing_none]
107#[derive(Serialize, Deserialize, Clone, Debug, Default, Builder)]
108#[serde(rename_all = "camelCase")]
109#[builder(setter(strip_option, into), default)]
110pub struct FocusStateRequestData {
111    pub state: FocusState,
112}
113
114#[skip_serializing_none]
115#[derive(Serialize, Deserialize, Clone, Debug, Default, Builder)]
116#[serde(rename_all = "camelCase")]
117#[builder(setter(strip_option, into), default)]
118pub struct GetItemsInfoRequestData {
119    pub item_ids: Vec<i32>,
120    pub media_session_id: Option<i32>,
121}
122
123#[skip_serializing_none]
124#[derive(Serialize, Deserialize, Clone, Debug, Default, Builder)]
125#[serde(rename_all = "camelCase")]
126#[builder(setter(strip_option, into), default)]
127pub struct GetStatusRequestData {
128    pub media_session_id: Option<i32>,
129    pub options: Option<GetStatusOptions>,
130}
131
132#[skip_serializing_none]
133#[derive(Serialize, Deserialize, Clone, Debug, Default, Builder)]
134#[serde(rename_all = "camelCase")]
135#[builder(setter(strip_option, into), default)]
136pub struct LoadByEntityRequestData {
137    pub entity: String,
138    pub shuffle: Option<bool>,
139}
140
141#[skip_serializing_none]
142#[derive(Serialize, Deserialize, Clone, Debug, Default, Builder)]
143#[serde(rename_all = "camelCase")]
144#[builder(setter(strip_option, into), default)]
145pub struct LoadRequestData {
146    pub active_track_ids: Option<Vec<i32>>,
147    pub autoplay: Option<bool>,
148    pub credentials: Option<String>,
149    pub credentials_type: Option<String>,
150    pub current_time: Option<f64>,
151    pub media: MediaInformation,
152    pub media_session_id: Option<i32>,
153    pub playback_rate: Option<f64>,
154    pub queue_data: Option<QueueData>,
155}
156
157#[skip_serializing_none]
158#[derive(Serialize, Deserialize, Clone, Debug, Default, Builder)]
159#[serde(rename_all = "camelCase")]
160#[builder(setter(strip_option, into), default)]
161pub struct PrecacheRequestData {
162    pub active_track_ids: Option<Vec<i32>>,
163    pub autoplay: Option<bool>,
164    pub credentials: Option<String>,
165    pub credentials_type: Option<String>,
166    pub current_time: Option<f64>,
167    pub media: MediaInformation,
168    pub media_session_id: Option<i32>,
169    pub playback_rate: Option<f64>,
170    pub precache_data: Option<String>,
171    pub queue_data: Option<QueueData>,
172}
173
174#[skip_serializing_none]
175#[derive(Serialize, Deserialize, Clone, Debug, Default, Builder)]
176#[serde(rename_all = "camelCase")]
177#[builder(setter(strip_option, into), default)]
178pub struct PreloadRequestData {
179    pub active_track_ids: Option<Vec<i32>>,
180    pub autoplay: Option<bool>,
181    pub credentials: Option<String>,
182    pub credentials_type: Option<String>,
183    pub current_time: Option<f64>,
184    pub item_id: i32,
185    pub media: MediaInformation,
186    pub media_session_id: Option<i32>,
187    pub playback_rate: Option<f64>,
188    pub queue_data: Option<QueueData>,
189}
190
191#[skip_serializing_none]
192#[derive(Serialize, Deserialize, Clone, Debug, Default, Builder)]
193#[serde(rename_all = "camelCase")]
194#[builder(setter(strip_option, into), default)]
195pub struct QueueInsertRequestData {
196    pub current_item_id: Option<i32>,
197    pub current_item_index: Option<i32>,
198    pub current_time: Option<f64>,
199    pub insert_before: Option<bool>,
200    pub items: Vec<QueueItem>,
201    pub media_session_id: Option<i32>,
202}
203
204#[skip_serializing_none]
205#[derive(Serialize, Deserialize, Clone, Debug, Default, Builder)]
206#[serde(rename_all = "camelCase")]
207#[builder(setter(strip_option, into), default)]
208pub struct QueueLoadRequestData {
209    pub current_time: Option<f64>,
210    pub items: Vec<QueueItem>,
211    pub media_session_id: Option<i32>,
212    pub repeat_mode: Option<RepeatMode>,
213    pub start_index: Option<i32>,
214}
215
216#[skip_serializing_none]
217#[derive(Serialize, Deserialize, Clone, Debug, Default, Builder)]
218#[serde(rename_all = "camelCase")]
219#[builder(setter(strip_option, into), default)]
220pub struct QueueRemoveRequestData {
221    pub current_item_id: Option<i32>,
222    pub current_time: Option<f64>,
223    pub item_ids: Vec<i32>,
224    pub media_session_id: Option<i32>,
225}
226
227#[skip_serializing_none]
228#[derive(Serialize, Deserialize, Clone, Debug, Default, Builder)]
229#[serde(rename_all = "camelCase")]
230#[builder(setter(strip_option, into), default)]
231pub struct QueueReorderRequestData {
232    pub current_item_id: Option<i32>,
233    pub current_time: Option<f64>,
234    pub insert_before: Option<bool>,
235    pub item_ids: Vec<i32>,
236    pub media_session_id: Option<i32>,
237}
238
239#[skip_serializing_none]
240#[derive(Serialize, Deserialize, Clone, Debug, Default, Builder)]
241#[serde(rename_all = "camelCase")]
242#[builder(setter(strip_option, into), default)]
243pub struct QueueUpdateRequestData {
244    pub current_item_id: Option<i32>,
245    pub current_time: Option<f64>,
246    pub items: Option<Vec<QueueItem>>,
247    pub jump: Option<i32>,
248    pub media_session_id: Option<i32>,
249    pub repeat_mode: Option<RepeatMode>,
250    pub shuffle: Option<bool>,
251}
252
253#[skip_serializing_none]
254#[derive(Serialize, Deserialize, Clone, Debug, Default, Builder)]
255#[serde(rename_all = "camelCase")]
256#[builder(setter(strip_option, into), default)]
257pub struct RefreshCredentialsRequestData {}
258
259#[skip_serializing_none]
260#[derive(Serialize, Deserialize, Clone, Debug, Default, Builder)]
261#[serde(rename_all = "camelCase")]
262#[builder(setter(strip_option, into), default)]
263pub struct RequestData {
264    pub media_session_id: Option<i32>,
265}
266
267#[skip_serializing_none]
268#[derive(Serialize, Deserialize, Clone, Debug, Default, Builder)]
269#[serde(rename_all = "camelCase")]
270#[builder(setter(strip_option, into), default)]
271pub struct ResumeSessionRequestData {
272    pub session_state: SessionState,
273}
274
275#[skip_serializing_none]
276#[derive(Serialize, Deserialize, Clone, Debug, Default, Builder)]
277#[serde(rename_all = "camelCase")]
278#[builder(setter(strip_option, into), default)]
279pub struct SeekRequestData {
280    pub current_time: Option<f64>,
281    pub media_session_id: Option<i32>,
282    pub relative_time: Option<f64>,
283}
284
285#[skip_serializing_none]
286#[derive(Serialize, Deserialize, Clone, Debug, Default, Builder)]
287#[serde(rename_all = "camelCase")]
288#[builder(setter(strip_option, into), default)]
289pub struct SetCredentialsRequestData {
290    pub credentials: String,
291    pub for_request: Option<i32>,
292    pub is_likely_3p_account_linked_user: Option<bool>,
293}
294
295#[skip_serializing_none]
296#[derive(Serialize, Deserialize, Clone, Debug, Default, Builder)]
297#[serde(rename_all = "camelCase")]
298#[builder(setter(strip_option, into), default)]
299pub struct SetPlaybackRateRequestData {
300    pub media_session_id: Option<i32>,
301    pub playback_rate: Option<u32>,
302    pub relative_playback_rate: Option<i32>,
303}
304
305#[skip_serializing_none]
306#[derive(Serialize, Deserialize, Clone, Debug, Default, Builder)]
307#[serde(rename_all = "camelCase")]
308#[builder(setter(strip_option, into), default)]
309pub struct StoreSessionRequestData {}
310
311#[skip_serializing_none]
312#[derive(Serialize, Deserialize, Clone, Debug, Default, Builder)]
313#[serde(rename_all = "camelCase")]
314#[builder(setter(strip_option, into), default)]
315pub struct UserActionRequestData {
316    pub clear: Option<bool>,
317    pub user_action: UserAction,
318    pub user_action_context: Option<UserActionContext>,
319}
320
321#[skip_serializing_none]
322#[derive(Serialize, Deserialize, Clone, Debug, Default, Builder)]
323#[serde(rename_all = "camelCase")]
324#[builder(setter(strip_option, into), default)]
325pub struct VolumeRequestData {
326    pub media_session_id: Option<i32>,
327    pub volume: Volume,
328}
329
330// RESPONSE DATA -------------------------------------------------
331
332#[skip_serializing_none]
333#[derive(Serialize, Deserialize, Clone, Debug, Default)]
334#[serde(rename_all = "camelCase")]
335pub struct ResponseData<T> {
336    pub status: Vec<T>,
337}
338
339impl<T: Clone + Default> ResponseData<T> {
340    pub fn first(&self) -> T {
341        self.status.first().cloned().unwrap()
342    }
343}
344
345#[skip_serializing_none]
346#[derive(Serialize, Deserialize, Clone, Debug, Default)]
347#[serde(rename_all = "camelCase")]
348pub struct ErrorResponseData {
349    pub reason: ErrorReason,
350}
351
352#[skip_serializing_none]
353#[derive(Serialize, Deserialize, Clone, Debug, Default)]
354#[serde(rename_all = "camelCase")]
355pub struct StoreSessionResponseData {
356    pub session_state: SessionState,
357}
358
359// STRUCTS ------------------------------------------------------
360
361#[skip_serializing_none]
362#[derive(Serialize, Deserialize, Clone, Debug, Default, Builder)]
363#[serde(rename_all = "camelCase")]
364#[builder(setter(strip_option, into), default)]
365pub struct AduioTrackInfo {
366    pub audio_codec: Option<String>,
367    pub num_audio_channels: Option<i32>,
368    pub spatial_audio: Option<bool>,
369}
370
371#[skip_serializing_none]
372#[derive(Serialize, Deserialize, Clone, Debug, Default, Builder)]
373#[serde(rename_all = "camelCase")]
374#[serde(tag = "type")]
375#[serde(rename = "AUDIOBOOK_CHAPTER")]
376#[builder(setter(strip_option, into), default)]
377pub struct AudiobookChapterMediaMetadata {
378    pub book_title: Option<String>,
379    pub chapter_number: Option<i32>,
380    pub chapter_title: Option<String>,
381    pub images: Option<Vec<Image>>,
382    pub subtitle: Option<String>,
383    pub title: Option<String>,
384}
385
386impl From<AudiobookChapterMediaMetadata> for MediaMetadata {
387    fn from(val: AudiobookChapterMediaMetadata) -> Self {
388        MediaMetadata {
389            metadata_type: MetadataType::AudiobookChapter(val),
390            ..Default::default()
391        }
392    }
393}
394
395#[skip_serializing_none]
396#[derive(Serialize, Deserialize, Clone, Debug, Default, Builder)]
397#[serde(rename_all = "camelCase")]
398#[builder(setter(strip_option, into), default)]
399pub struct AudiobookContainerMetadata {
400    pub authors: Option<Vec<String>>,
401    pub narrators: Option<Vec<String>>,
402    pub publisher: Option<String>,
403    pub release_date: Option<String>,
404}
405
406#[skip_serializing_none]
407#[derive(Serialize, Deserialize, Clone, Debug, Default, Builder)]
408#[serde(rename_all = "camelCase")]
409#[builder(setter(strip_option, into), default)]
410pub struct Break {
411    pub break_clip_ids: Vec<String>,
412    pub duration: Option<f64>,
413    pub id: String,
414    pub is_embedded: Option<bool>,
415    pub is_watched: bool,
416    pub position: f64,
417}
418
419#[skip_serializing_none]
420#[derive(Serialize, Deserialize, Clone, Debug, Default, Builder)]
421#[serde(rename_all = "camelCase")]
422#[builder(setter(strip_option, into), default)]
423pub struct BreakClip {
424    pub click_through_url: Option<String>,
425    pub content_id: Option<String>,
426    pub content_type: Option<String>,
427    pub content_url: Option<String>,
428    pub duration: Option<f64>,
429    pub hls_segment_format: Option<HlsSegmentFormat>,
430    pub id: String,
431    pub poster_url: Option<String>,
432    pub title: Option<String>,
433    pub vast_ads_request: Option<VastAdsRequest>,
434    pub when_skippable: Option<f64>,
435}
436
437#[skip_serializing_none]
438#[derive(Serialize, Deserialize, Clone, Debug, Default, Builder)]
439#[serde(rename_all = "camelCase")]
440#[builder(setter(strip_option, into), default)]
441pub struct BreakStatus {
442    pub break_clip_id: Option<String>,
443    pub break_id: Option<String>,
444    pub current_break_clip_time: Option<f64>,
445    pub current_break_time: Option<f64>,
446    pub when_skippable: Option<f64>,
447}
448
449#[skip_serializing_none]
450#[derive(Serialize, Deserialize, Clone, Debug, Default, Builder)]
451#[serde(rename_all = "camelCase")]
452#[builder(setter(strip_option, into), default)]
453pub struct CloudMediaStatus {}
454
455#[skip_serializing_none]
456#[derive(Serialize, Deserialize, Clone, Debug, Default, Builder)]
457#[serde(rename_all = "camelCase")]
458#[builder(setter(strip_option, into), default)]
459pub struct ContainerMetadata {
460    pub container_duration: Option<f64>,
461    pub container_images: Option<Vec<Image>>,
462    pub container_type: ContainerType,
463    pub sections: Option<Vec<MediaMetadata>>,
464    pub title: Option<String>,
465}
466
467#[skip_serializing_none]
468#[derive(Serialize, Deserialize, Clone, Debug, Default, Builder)]
469#[serde(rename_all = "camelCase")]
470#[builder(setter(strip_option, into), default)]
471pub struct ExtendedMediaStatus {
472    pub player_state: ExtendedPlayerState,
473    pub media: Option<MediaInformation>,
474    pub media_session_id: Option<i32>,
475}
476
477#[skip_serializing_none]
478#[derive(Serialize, Deserialize, Clone, Debug, Default, Builder)]
479#[serde(rename_all = "camelCase")]
480#[serde(tag = "type")]
481#[serde(rename = "GENERIC")]
482#[builder(setter(strip_option, into), default)]
483pub struct GenericMediaMetadata {
484    pub images: Option<Vec<Image>>,
485    pub release_date: Option<String>,
486    pub subtitle: Option<String>,
487    pub title: Option<String>,
488}
489
490impl From<GenericMediaMetadata> for MediaMetadata {
491    fn from(val: GenericMediaMetadata) -> Self {
492        MediaMetadata {
493            metadata_type: MetadataType::Generic(val),
494            ..Default::default()
495        }
496    }
497}
498
499#[skip_serializing_none]
500#[derive(Serialize, Deserialize, Clone, Debug, Default, Builder)]
501#[serde(rename_all = "camelCase")]
502#[builder(setter(strip_option, into), default)]
503pub struct ItemsInfo {
504    pub items: Option<Vec<QueueItem>>,
505}
506
507#[skip_serializing_none]
508#[derive(Serialize, Deserialize, Clone, Debug, Default, Builder)]
509#[serde(rename_all = "camelCase")]
510#[builder(setter(strip_option, into), default)]
511pub struct LiveSeekableRange {
512    pub end: Option<f64>,
513    pub is_live_done: Option<bool>,
514    pub is_moving_window: Option<bool>,
515    pub start: Option<f64>,
516}
517
518#[skip_serializing_none]
519#[derive(Serialize, Deserialize, Clone, Debug, Default, Builder)]
520#[serde(rename_all = "camelCase")]
521#[builder(setter(strip_option, into), default)]
522pub struct MediaInformation {
523    pub atv_entity: Option<String>,
524    pub break_clips: Option<Vec<BreakClip>>,
525    pub breaks: Option<Vec<Break>>,
526    pub content_id: String,
527    pub content_type: String,
528    pub duration: Option<f64>,
529    pub entity: Option<String>,
530    pub hls_segment_format: Option<HlsSegmentFormat>,
531    pub hls_video_segment_format: Option<HlsVideoSegmentFormat>,
532    pub metadata: Option<MediaMetadata>,
533    pub start_absolute_time: Option<f64>,
534    pub stream_type: StreamType,
535    pub text_track_style: Option<TextTrackStyle>,
536    pub tracks: Option<Vec<Track>>,
537    pub user_action_states: Option<Vec<UserActionState>>,
538    pub vmap_ads_request: Option<VastAdsRequest>,
539}
540
541impl From<MediaInformation> for LoadRequestData {
542    fn from(val: MediaInformation) -> Self {
543        LoadRequestData {
544            media: val,
545            autoplay: Some(true),
546            ..Default::default()
547        }
548    }
549}
550
551#[skip_serializing_none]
552#[derive(Serialize, Deserialize, Clone, Debug, Default, Builder)]
553#[serde(rename_all = "camelCase")]
554#[builder(setter(strip_option, into), default)]
555pub struct MediaMetadata {
556    #[serde(flatten)]
557    pub metadata_type: MetadataType,
558    pub poster_url: Option<String>,
559    pub queue_item_id: Option<i32>,
560    pub section_duration: Option<f64>,
561    pub section_start_absolute_time: Option<f64>,
562    pub section_start_time_in_container: Option<f64>,
563    pub section_start_time_in_media: Option<f64>,
564}
565
566#[skip_serializing_none]
567#[derive(Serialize, Deserialize, Clone, Debug, Default, Builder)]
568#[serde(rename_all = "camelCase")]
569#[builder(setter(strip_option, into), default)]
570pub struct MediaStatus {
571    pub active_track_ids: Option<Vec<i32>>,
572    pub break_status: Option<BreakStatus>,
573    pub current_item_id: Option<i32>,
574    pub current_time: f64,
575    pub extended_status: Option<ExtendedMediaStatus>,
576    pub idle_reason: Option<IdleReason>,
577    pub items: Option<Vec<QueueItem>>,
578    pub live_seekable_range: Option<LiveSeekableRange>,
579    pub loading_item_id: Option<i32>,
580    pub media: Option<MediaInformation>,
581    pub media_session_id: i32,
582    pub playback_rate: i32,
583    pub player_state: PlayerState,
584    pub preloaded_item_id: Option<i32>,
585    pub queue_data: Option<QueueData>,
586    pub repeat_mode: Option<RepeatMode>,
587    pub supported_media_commands: Command,
588    pub video_info: Option<VideoInformation>,
589    pub volume: Volume,
590}
591
592#[skip_serializing_none]
593#[derive(Serialize, Deserialize, Clone, Debug, Default, Builder)]
594#[serde(rename_all = "camelCase")]
595#[serde(tag = "type")]
596#[serde(rename = "MOVIE")]
597#[builder(setter(strip_option, into), default)]
598pub struct MovieMediaMetadata {
599    pub images: Option<Vec<Image>>,
600    pub release_date: Option<String>,
601    pub studio: Option<String>,
602    pub subtitle: Option<String>,
603    pub title: Option<String>,
604}
605
606impl From<MovieMediaMetadata> for MediaMetadata {
607    fn from(val: MovieMediaMetadata) -> Self {
608        MediaMetadata {
609            metadata_type: MetadataType::Movie(val),
610            ..Default::default()
611        }
612    }
613}
614
615#[skip_serializing_none]
616#[derive(Serialize, Deserialize, Clone, Debug, Default, Builder)]
617#[serde(rename_all = "camelCase")]
618#[serde(tag = "type")]
619#[serde(rename = "MUSIC_TRACK")]
620#[builder(setter(strip_option, into), default)]
621pub struct MusicTrackMediaMetadata {
622    pub album_artist: Option<String>,
623    pub album_name: Option<String>,
624    pub artist: Option<String>,
625    pub composer: Option<String>,
626    pub disc_number: Option<i32>,
627    pub images: Option<Vec<Image>>,
628    pub release_date: Option<String>,
629    pub secondary_image: Option<Image>,
630    pub title: Option<String>,
631    pub track_number: Option<i32>,
632}
633
634impl From<MusicTrackMediaMetadata> for MediaMetadata {
635    fn from(val: MusicTrackMediaMetadata) -> Self {
636        MediaMetadata {
637            metadata_type: MetadataType::MusicTrack(val),
638            ..Default::default()
639        }
640    }
641}
642
643#[skip_serializing_none]
644#[derive(Serialize, Deserialize, Clone, Debug, Default, Builder)]
645#[serde(rename_all = "camelCase")]
646#[serde(tag = "type")]
647#[serde(rename = "PHOTO")]
648#[builder(setter(strip_option, into), default)]
649pub struct PhotoMediaMetadata {
650    pub artist: Option<String>,
651    pub creation_date_time: Option<String>,
652    pub height: Option<u32>,
653    pub images: Option<Vec<Image>>,
654    pub latitude: Option<i32>,
655    pub location: Option<String>,
656    pub longitude: Option<i32>,
657    pub width: Option<u32>,
658}
659
660impl From<PhotoMediaMetadata> for MediaMetadata {
661    fn from(val: PhotoMediaMetadata) -> Self {
662        MediaMetadata {
663            metadata_type: MetadataType::Photo(val),
664            ..Default::default()
665        }
666    }
667}
668
669#[skip_serializing_none]
670#[derive(Serialize, Deserialize, Clone, Debug, Default, Builder)]
671#[serde(rename_all = "camelCase")]
672#[builder(setter(strip_option, into), default)]
673pub struct QueueChange {
674    pub change_type: Option<QueueChangeType>,
675    pub insert_before: Option<i32>,
676    pub item_ids: Option<Vec<i32>>,
677    pub reorder_item_ids: Option<Vec<i32>>,
678}
679
680#[skip_serializing_none]
681#[derive(Serialize, Deserialize, Clone, Debug, Default, Builder)]
682#[serde(rename_all = "camelCase")]
683#[builder(setter(strip_option, into), default)]
684pub struct QueueData {
685    pub container_metadata: Option<ContainerMetadata>,
686    pub description: Option<String>,
687    pub entity: Option<String>,
688    pub id: Option<String>,
689    pub items: Option<Vec<QueueItem>>,
690    pub name: Option<String>,
691    pub queue_type: Option<QueueType>,
692    pub repeat_mode: Option<RepeatMode>,
693    pub shuffle: Option<bool>,
694    pub start_index: Option<i32>,
695    pub start_time: Option<f64>,
696}
697
698#[skip_serializing_none]
699#[derive(Serialize, Deserialize, Clone, Debug, Default, Builder)]
700#[serde(rename_all = "camelCase")]
701#[builder(setter(strip_option, into), default)]
702pub struct QueueIds {
703    pub item_ids: Option<Vec<i32>>,
704}
705
706#[skip_serializing_none]
707#[derive(Serialize, Deserialize, Clone, Debug, Default, Builder)]
708#[serde(rename_all = "camelCase")]
709#[builder(setter(strip_option, into), default)]
710pub struct SeekableRange {
711    pub end: Option<f64>,
712    pub start: Option<f64>,
713}
714
715#[skip_serializing_none]
716#[derive(Serialize, Deserialize, Clone, Debug, Default, Builder)]
717#[serde(rename_all = "camelCase")]
718#[builder(setter(strip_option, into), default)]
719pub struct SessionState {
720    pub load_request_data: Option<LoadRequestData>,
721}
722
723#[skip_serializing_none]
724#[derive(Serialize, Deserialize, Clone, Debug, Default, Builder)]
725#[serde(rename_all = "camelCase")]
726#[builder(setter(strip_option, into), default)]
727pub struct QueueItem {
728    pub active_track_ids: Option<Vec<i32>>,
729    pub autoplay: Option<bool>,
730    pub item_id: Option<i32>,
731    pub media: Option<MediaInformation>,
732    pub playback_duration: Option<f64>,
733    pub preload_time: Option<f64>,
734    pub start_time: Option<f64>,
735}
736
737#[skip_serializing_none]
738#[derive(Serialize, Deserialize, Clone, Debug, Default, Builder)]
739#[serde(rename_all = "camelCase")]
740#[builder(setter(strip_option, into), default)]
741pub struct TextTrackStyle {
742    pub background_color: Option<String>,
743    pub edge_color: Option<String>,
744    pub edge_type: Option<TextTrackEdgeType>,
745    pub font_family: Option<String>,
746    pub font_generic_family: Option<TextTrackFontGenericFamily>,
747    pub font_scale: Option<i32>,
748    pub font_style: Option<TextTrackFontStyle>,
749    pub foreground_color: Option<String>,
750    pub window_color: Option<String>,
751    pub window_rounded_corner_radius: Option<f64>,
752    pub window_type: Option<TextTrackWindowType>,
753}
754
755#[skip_serializing_none]
756#[derive(Serialize, Deserialize, Clone, Debug, Default, Builder)]
757#[serde(rename_all = "camelCase")]
758#[builder(setter(strip_option, into), default)]
759pub struct Track {
760    pub audio_track_info: Option<AduioTrackInfo>,
761    pub is_inband: Option<bool>,
762    pub language: Option<String>,
763    pub name: Option<String>,
764    pub roles: Option<Vec<String>>,
765    pub subtype: Option<TextTrackType>,
766    pub track_content_id: Option<String>,
767    pub track_content_type: Option<CaptionMimeType>,
768    pub track_id: i32,
769    #[serde(rename = "type")]
770    pub type_: TrackType,
771}
772
773#[skip_serializing_none]
774#[derive(Serialize, Deserialize, Clone, Debug, Default, Builder)]
775#[serde(rename_all = "camelCase")]
776#[builder(setter(strip_option, into), default)]
777pub struct TracksInfo {
778    pub active_track_ids: Option<Vec<i32>>,
779    pub language: Option<String>,
780    pub text_track_style: Option<TextTrackStyle>,
781    pub tracks: Option<Vec<Track>>,
782}
783
784#[skip_serializing_none]
785#[derive(Serialize, Deserialize, Clone, Debug, Default, Builder)]
786#[serde(rename_all = "camelCase")]
787#[serde(tag = "type")]
788#[serde(rename = "TV_SHOW")]
789#[builder(setter(strip_option, into), default)]
790pub struct TvShowMediaMetadata {
791    pub episode: Option<i32>,
792    pub episode_title: Option<String>,
793    pub images: Option<Vec<Image>>,
794    pub original_airdate: Option<String>,
795    pub season: Option<i32>,
796    pub series_title: Option<String>,
797    pub title: Option<String>,
798}
799
800impl From<TvShowMediaMetadata> for MediaMetadata {
801    fn from(val: TvShowMediaMetadata) -> Self {
802        MediaMetadata {
803            metadata_type: MetadataType::TvShow(val),
804            ..Default::default()
805        }
806    }
807}
808
809#[skip_serializing_none]
810#[derive(Serialize, Deserialize, Clone, Debug, Default, Builder)]
811#[serde(rename_all = "camelCase")]
812#[builder(setter(strip_option, into), default)]
813pub struct UserActionState {
814    pub user_action: UserAction,
815}
816
817#[derive(Serialize, Deserialize, Clone, Debug, Default)]
818#[serde(rename_all = "camelCase")]
819pub struct VastAdsRequest {
820    pub ads_response: Option<String>,
821    pub ad_tag_url: Option<String>,
822}
823
824#[skip_serializing_none]
825#[derive(Serialize, Deserialize, Clone, Debug, Default, Builder)]
826#[serde(rename_all = "camelCase")]
827#[builder(setter(strip_option, into), default)]
828pub struct VideoInformation {
829    pub hdr_type: HdrType,
830    pub height: i32,
831    pub width: i32,
832}
833
834// ENUMS --------------------------------------------------------
835
836#[derive(Serialize, Deserialize, Clone, Debug, Default)]
837#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
838pub enum CaptionMimeType {
839    #[default]
840    #[serde(alias = "cea608")]
841    Cea608,
842    #[serde(alias = "ttml")]
843    Ttml,
844    #[serde(alias = "vtt")]
845    Vtt,
846    #[serde(alias = "ttmlmp3")]
847    TtmlMp3,
848    #[serde(untagged)]
849    Other(String),
850}
851
852#[derive(Serialize, Deserialize, Default)]
853#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
854pub enum CommandOld {
855    #[default]
856    Pause,
857    Seek,
858    StreamVolume,
859    StreamMute,
860    AllBasicMedia,
861    QueueNext,
862    QueuePrev,
863    QueueShuffle,
864    QueueRepeatAll,
865    QueueRepeatOne,
866    QueueRepeat,
867    SkipAd,
868    EditTracks,
869    PlaybackRate,
870    Like,
871    Dislike,
872    Follow,
873    Unfollow,
874    StreamTransfer,
875    Lyrics,
876}
877
878bitflags::bitflags! {
879    #[derive(Debug, PartialEq, Eq, Clone, Default)]
880    pub struct Command: u32 {
881        const Pause = 1;
882        const Seek = 2;
883        const StreamVolume = 4;
884        const StreamMute = 8;
885        const AllBasicMedia = 12303;
886        const QueueNext = 64;
887        const QueuePrev = 128;
888        const QueueShuffle = 256;
889        const QueueRepeatAll = 1024;
890        const QueueRepeatOne = 2048;
891        const QueueRepeat = 3072;
892        const SkipAd = 512;
893        const EditTracks = 4096;
894        const PlaybackRate = 8192;
895        const Like = 16384;
896        const Dislike = 32768;
897        const Follow = 65536;
898        const Unfollow = 131072;
899        const StreamTransfer = 262144;
900        const Lyrics = 524288;
901    }
902}
903
904impl serde::Serialize for Command {
905    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
906    where
907        S: serde::Serializer,
908    {
909        serializer.serialize_u32(self.bits())
910    }
911}
912
913impl<'de> serde::Deserialize<'de> for Command {
914    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
915    where
916        D: serde::Deserializer<'de>,
917    {
918        let bits = u32::deserialize(deserializer)?;
919        Command::from_bits(bits).ok_or_else(|| serde::de::Error::custom("invalid bitflags value"))
920    }
921}
922
923#[derive(Serialize, Deserialize, Clone, Debug, Default)]
924#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
925pub enum ContainerType {
926    #[default]
927    GenericContainer,
928    AudiobookContainer,
929}
930
931#[derive(Serialize, Deserialize, Clone, Debug, Default)]
932#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
933pub enum ContentFilteringMode {
934    #[default]
935    FilterExplicit,
936}
937
938#[derive(Serialize, Deserialize, Clone, Debug, Default)]
939#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
940pub enum ErrorReason {
941    #[default]
942    None,
943    InvalidCommand,
944    InvalidParams,
945    InvalidMediaSessionId,
946    InvalidRequestId,
947    SkipLimitReached,
948    NotSupported,
949    LanguageNotSupported,
950    EndOfQueue,
951    DuplicateRequestId,
952    VideoDeviceRequired,
953    PremiumAccountRequired,
954    AppError,
955    AuthenticationExpired,
956    ConcurrentStreamLimit,
957    ParentalControlRestricted,
958    ContentFiltered,
959    NotAvailableInRegion,
960    ContentAlreadyPlaying,
961    InvalidRequest,
962    GenericLoadError,
963}
964
965#[derive(Serialize, Deserialize, Clone, Debug, Default)]
966#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
967pub enum ExtendedPlayerState {
968    #[default]
969    Loading,
970}
971
972#[derive(Serialize, Deserialize, Clone, Debug, Default)]
973#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
974pub enum FocusState {
975    #[default]
976    InFocus,
977    NotInFocus,
978}
979
980#[derive(Serialize, Deserialize, Clone, Debug, Default)]
981#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
982pub enum GetStatusOptions {
983    #[default]
984    NoMetadata,
985    NoQueueItems,
986}
987
988#[derive(Serialize, Deserialize, Clone, Debug, Default)]
989#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
990pub enum HdrType {
991    #[default]
992    #[serde(alias = "sdr")]
993    Sdr,
994    #[serde(alias = "hdr")]
995    Hdr,
996    #[serde(alias = "dv")]
997    Dv,
998}
999
1000#[derive(Serialize, Deserialize, Clone, Debug, Default)]
1001#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
1002pub enum HlsSegmentFormat {
1003    #[default]
1004    Aac,
1005    Ac3,
1006    Mp3,
1007    Ts,
1008    TsAac,
1009    EAc3,
1010    Fmp4,
1011}
1012
1013#[derive(Serialize, Deserialize, Clone, Debug, Default)]
1014#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
1015pub enum HlsVideoSegmentFormat {
1016    #[default]
1017    Mpeg2Ts,
1018    Fmp4,
1019}
1020
1021#[derive(Serialize, Deserialize, Clone, Debug, Default)]
1022#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
1023pub enum IdleReason {
1024    #[default]
1025    Cancelled,
1026    Interrupted,
1027    Finished,
1028    Error,
1029}
1030
1031#[derive(Serialize, Deserialize, Clone, Debug, Default)]
1032#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
1033pub enum MediaCategory {
1034    #[default]
1035    Audio,
1036    Video,
1037    Image,
1038}
1039
1040#[derive(Serialize, Deserialize, Clone, Debug)]
1041#[serde(untagged)]
1042pub enum MetadataType {
1043    Generic(GenericMediaMetadata),
1044    Movie(MovieMediaMetadata),
1045    TvShow(TvShowMediaMetadata),
1046    MusicTrack(MusicTrackMediaMetadata),
1047    Photo(PhotoMediaMetadata),
1048    AudiobookChapter(AudiobookChapterMediaMetadata),
1049}
1050
1051impl Default for MetadataType {
1052    fn default() -> Self {
1053        Self::Generic(GenericMediaMetadata::default())
1054    }
1055}
1056
1057#[derive(Serialize, Deserialize, Clone, Debug, Default)]
1058#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
1059pub enum PlayerState {
1060    #[default]
1061    Idle,
1062    Playing,
1063    Paused,
1064    Buffering,
1065}
1066
1067#[derive(Serialize, Deserialize, Clone, Debug, Default)]
1068#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
1069pub enum QueueChangeType {
1070    #[default]
1071    Insert,
1072    Remove,
1073    ItemsChange,
1074    Update,
1075    NoChange,
1076}
1077
1078#[derive(Serialize, Deserialize, Clone, Debug, Default)]
1079#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
1080pub enum QueueType {
1081    #[default]
1082    Album,
1083    Playlist,
1084    Audiobook,
1085    RadioStation,
1086    PodcastSeries,
1087    TvSeries,
1088    VideoPlaylist,
1089    LiveTv,
1090    Movie,
1091}
1092
1093#[derive(Serialize, Deserialize, Clone, Debug, Default)]
1094#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
1095pub enum RepeatMode {
1096    #[default]
1097    RepeatOff,
1098    RepeatAll,
1099    RepeatSingle,
1100    RepeatAllAndShuffle,
1101}
1102
1103#[derive(Serialize, Deserialize, Clone, Debug, Default)]
1104#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
1105pub enum SeekResumeState {
1106    #[default]
1107    PlaybackStart,
1108    PlaybackPause,
1109}
1110
1111#[derive(Serialize, Deserialize, Clone, Debug, Default)]
1112#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
1113pub enum StreamingProtocolType {
1114    #[default]
1115    Unknown,
1116    MpegDash,
1117    Hls,
1118    SmoothStreaming,
1119}
1120
1121#[derive(Serialize, Deserialize, Clone, Debug, Default)]
1122#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
1123pub enum StreamType {
1124    #[default]
1125    None,
1126    Buffered,
1127    Live,
1128}
1129
1130#[derive(Serialize, Deserialize, Clone, Debug, Default)]
1131#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
1132pub enum TextTrackEdgeType {
1133    #[default]
1134    None,
1135    Outline,
1136    DropShadow,
1137    Raised,
1138    Depressed,
1139}
1140
1141#[derive(Serialize, Deserialize, Clone, Debug, Default)]
1142#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
1143pub enum TextTrackFontGenericFamily {
1144    #[default]
1145    SansSerif,
1146    MonospacedSansSerif,
1147    Serif,
1148    MonospacedSerif,
1149    Casual,
1150    Cursive,
1151    SmallCapitals,
1152}
1153
1154#[derive(Serialize, Deserialize, Clone, Debug, Default)]
1155#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
1156pub enum TextTrackFontStyle {
1157    #[default]
1158    Normal,
1159    Bold,
1160    BoldItalic,
1161    Italic,
1162}
1163
1164#[derive(Serialize, Deserialize, Clone, Debug, Default)]
1165#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
1166pub enum TextTrackType {
1167    #[default]
1168    Subtitles,
1169    Captions,
1170    Descriptions,
1171    Chapters,
1172    Metadata,
1173}
1174
1175#[derive(Serialize, Deserialize, Clone, Debug, Default)]
1176#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
1177pub enum TextTrackWindowType {
1178    #[default]
1179    None,
1180    Normal,
1181    RoundedCorners,
1182}
1183
1184#[derive(Serialize, Deserialize, Clone, Debug, Default)]
1185#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
1186pub enum TrackType {
1187    #[default]
1188    Text,
1189    Audio,
1190    Video,
1191}
1192
1193#[derive(Serialize, Deserialize, Clone, Debug, Default)]
1194#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
1195pub enum UserAction {
1196    #[default]
1197    Like,
1198    Dislike,
1199    Follow,
1200    Unfollow,
1201    Flag,
1202    SkipAd,
1203    Lyrics,
1204}
1205
1206#[derive(Serialize, Deserialize, Clone, Debug, Default)]
1207#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
1208pub enum UserActionContext {
1209    #[default]
1210    UnknownContext,
1211    Track,
1212    Album,
1213    Artist,
1214    Playlist,
1215    Episode,
1216    Series,
1217    Movie,
1218    Channel,
1219    Team,
1220    Player,
1221    Coach,
1222}
1223
1224#[cfg(test)]
1225mod test {
1226    use crate::namespace::media::{CaptionMimeType, Media};
1227
1228    #[test]
1229    fn test_deserialize_media() {
1230        let payload: Media = serde_json::from_str(
1231            r#"
1232            {
1233                "type": "MEDIA_STATUS",
1234                "status": [
1235                    {
1236                        "activeTrackIds": [7],
1237                        "currentItemId": 1,
1238                        "currentTime": 0,
1239                        "items": [
1240                            {
1241                            "activeTrackIds": [2, 7],
1242                            "autoplay": true,
1243                            "itemId": 1,
1244                            "media": {
1245                                "contentId": "http://host.com/stream",
1246                                "contentType": "video/x-matroska",
1247                                "duration": 1422.004,
1248                                "mediaCategory": "VIDEO",
1249                                "streamType": "BUFFERED",
1250                                "tracks": [
1251                                    {
1252                                        "language": "eng",
1253                                        "name": "Audio",
1254                                        "trackContentId": "trk0001",
1255                                        "trackContentType": "audio/aac",
1256                                        "trackId": 2,
1257                                        "type": "AUDIO"
1258                                    }, 
1259                                    {
1260                                        "language": "eng",
1261                                        "name": "Subtitle",
1262                                        "subtype": "SUBTITLES",
1263                                        "trackContentId": "http://host.com/stream/subtitle",
1264                                        "trackContentType": "text/vtt",
1265                                        "trackId": 7,
1266                                        "type": "TEXT"
1267                                    }
1268                                ]
1269                            },
1270                            "orderId": 0
1271                        }],
1272                        "mediaSessionId": 29,
1273                        "playbackRate": 1,
1274                        "playerState": "PLAYING",
1275                        "repeatMode": "REPEAT_OFF",
1276                        "supportedMediaCommands": 274447,
1277                        "videoInfo": {"hdrType": "sdr", "height": 1080, "width": 1920},
1278                        "volume": {"level": 1, "muted": false}
1279                    }
1280                ]
1281            }
1282        "#,
1283        )
1284        .unwrap();
1285        assert!(
1286            matches!(payload, Media::MediaStatus(_)),
1287            "Failed: {payload:#?}"
1288        );
1289    }
1290
1291    #[test]
1292    fn test_caption_mime() {
1293        let cap: CaptionMimeType = serde_json::from_str(r#""garbage""#).unwrap();
1294        let _garbage = "garbage".to_string();
1295        assert!(matches!(cap, CaptionMimeType::Other(_garbage)));
1296
1297        let cap: CaptionMimeType = serde_json::from_str(r#""VTT""#).unwrap();
1298        assert!(matches!(cap, CaptionMimeType::Vtt));
1299
1300        let cap: CaptionMimeType = serde_json::from_str(r#""vtt""#).unwrap();
1301        assert!(matches!(cap, CaptionMimeType::Vtt));
1302    }
1303}