mochi_rs/structs/
video.rs

1extern crate alloc;
2
3use alloc::vec;
4use alloc::{string::String, vec::Vec};
5
6use crate::std::{error::Result, PtrRef, ArrayRef, ObjectRef};
7use super::conversion::optional_str_ptr;
8
9use super::meta::{PlaylistItemsRequest, PlaylistItemsResponse};
10
11#[link(wasm_import_module = "structs_video")]
12extern "C" {
13    fn create_episode_source(
14        id_ptr: i32,
15        id_len: i32,
16        display_name_ptr: i32,
17        display_name_len: i32,
18        description_ptr: i32,
19        description_len: i32,
20        servers_ptr: i32
21    ) -> i32;
22
23    fn create_episode_server(
24        id_ptr: i32,
25        id_len: i32,
26        display_name_ptr: i32,
27        display_name_len: i32,
28        description_ptr: i32,
29        description_len: i32
30    ) -> i32;
31
32    fn create_episode_server_response(
33        links_ptr: i32,
34        subtitles_ptr: i32,
35        skip_times_ptr: i32,
36        headers_ptr: i32
37    ) -> i32;
38
39    fn create_episode_server_link(
40        url_ptr: i32,
41        url_len: i32,
42        quality: i32,
43        format: i32
44    ) -> i32;
45
46    fn create_episode_server_subtitle(
47        url_ptr: i32,
48        url_len: i32,
49        name_ptr: i32,
50        name_len: i32,
51        format: i32,
52        default: bool,
53        autoselect: bool
54    ) -> i32;
55
56    fn create_episode_server_skip_time(
57        start_time: f32,
58        end_time: f32,
59        skip_type: PlaylistEpisodeServerSkipType
60    ) -> i32;
61}
62
63pub trait Video {
64    fn playlist_episodes(request: PlaylistItemsRequest) -> Result<PlaylistItemsResponse>;
65    fn playlist_episode_sources(request: PlaylistEpisodeSourcesRequest) -> Result<PlaylistEpisodeSources>;
66    fn playlist_episode_server(request: PlaylistEpisodeServerRequest) -> Result<PlaylistEpisodeServerResponse>;
67}
68
69pub struct PlaylistEpisodeSourcesRequest {
70    pub playlist_id: String,
71    pub episode_id: String
72}
73
74pub struct PlaylistEpisodeSources(pub Vec<PlaylistEpisodeSource>);
75
76pub struct PlaylistEpisodeSource {
77    pub id: String,
78    pub display_name: String,
79    pub description: Option<String>,
80    pub servers: Vec<PlaylistEpisodeServer>
81}
82
83impl Default for PlaylistEpisodeSource {
84    fn default() -> Self {
85        Self { 
86            id: "default".into(), 
87            display_name: "Default".into(), 
88            description: None, 
89            servers: vec![] 
90        }
91    }
92}
93
94pub struct PlaylistEpisodeServer {
95    pub id: String,
96    pub display_name: String,
97    pub description: Option<String>
98}
99
100pub struct PlaylistEpisodeServerRequest {
101    pub playlist_id: String,
102    pub episode_id: String,
103    pub source_id: String,
104    pub server_id: String
105}
106
107pub struct PlaylistEpisodeServerResponse {
108    pub links: Vec<PlaylistEpisodeServerLink>,
109    pub subtitles: Vec<PlaylistEpisodeServerSubtitle>,
110    pub skip_times: Vec<PlaylistEpisodeServerSkipTime>,
111    pub headers: Vec<PlaylistEpisodeServerHeader>
112}
113
114#[repr(C)]
115#[derive(PartialEq, Eq, Debug, Clone, Copy)]
116pub enum PlaylistEpisodeServerFormatType {
117    HLS,
118    DASH
119}
120
121pub struct PlaylistEpisodeServerLink {
122    pub url: String,
123    pub quality: PlaylistEpisodeServerQualityType,
124    pub format_type: PlaylistEpisodeServerFormatType,
125}
126
127#[repr(u32)]
128pub enum PlaylistEpisodeServerQualityType {
129    Auto,
130    Q1080p,
131    Q720p,
132    Q480p,
133    Q360p,
134    Custom(u32)
135}
136
137impl Into<i32> for PlaylistEpisodeServerQualityType {
138    fn into(self) -> i32 {
139        match self {
140            PlaylistEpisodeServerQualityType::Auto => 0,
141            PlaylistEpisodeServerQualityType::Q1080p => 1080,
142            PlaylistEpisodeServerQualityType::Q720p => 720,
143            PlaylistEpisodeServerQualityType::Q480p => 480,
144            PlaylistEpisodeServerQualityType::Q360p => 360,
145            PlaylistEpisodeServerQualityType::Custom(v) => v as i32,
146        }
147    }
148}
149
150pub struct PlaylistEpisodeServerSubtitle {
151    pub url: String,
152    pub name: String,
153    pub format: PlaylistEpisodeServerSubtitleFormat,
154    pub default: bool,
155    pub autoselect: bool,
156}
157
158#[repr(C)]
159#[derive(PartialEq, Eq, Debug, Clone, Copy)]
160pub enum PlaylistEpisodeServerSubtitleFormat {
161    VTT,
162    ASS,
163    SRT
164}
165
166pub struct PlaylistEpisodeServerSkipTime {
167    /// Start time in seconds
168    pub start_time: f32,
169
170    /// End time in seconds
171    pub end_time: f32,
172
173    /// Skip type
174    pub skip_type: PlaylistEpisodeServerSkipType
175}
176
177#[repr(C)]
178#[derive(PartialEq, Eq, Debug, Clone, Copy)]
179pub enum PlaylistEpisodeServerSkipType {
180    OPENING,
181    ENDING,
182    RECAP
183}
184
185pub struct PlaylistEpisodeServerHeader {
186    pub key: String,
187    pub value: String
188}
189
190// Into + From Implementations
191
192impl Into<PlaylistEpisodeSourcesRequest> for PtrRef {
193    fn into(self) -> PlaylistEpisodeSourcesRequest {
194        if let Ok(obj_ref) = self.as_object() {
195            let playlist_id = obj_ref.get("playlistId").as_string()
196                .unwrap_or_default();
197            let episode_id = obj_ref.get("episodeId").as_string()
198                .unwrap_or_default();
199            PlaylistEpisodeSourcesRequest {
200                playlist_id,
201                episode_id
202            }
203        } else {
204            PlaylistEpisodeSourcesRequest {
205                playlist_id: "".into(),
206                episode_id: "".into(),
207            }    
208        }
209    }
210}
211
212impl Into<PlaylistEpisodeServerRequest> for PtrRef {
213    fn into(self) -> PlaylistEpisodeServerRequest {
214        if let Ok(obj_ref) = self.as_object() {
215            let playlist_id = obj_ref.get("playlistId").as_string().unwrap_or_default();
216            let episode_id = obj_ref.get("episodeId").as_string().unwrap_or_default();
217            let source_id = obj_ref.get("sourceId").as_string().unwrap_or_default();
218            let server_id = obj_ref.get("serverId").as_string().unwrap_or_default();
219            PlaylistEpisodeServerRequest {
220                playlist_id,
221                episode_id,
222                source_id,
223                server_id
224            }
225        } else {
226            PlaylistEpisodeServerRequest {
227                playlist_id: "".into(),
228                episode_id: "".into(),
229                source_id: "".into(),
230                server_id: "".into()
231            }    
232        }        
233    }
234}
235
236impl From<PlaylistEpisodeSources> for PtrRef {
237    fn from(value: PlaylistEpisodeSources) -> Self {
238        let sources = ArrayRef::from(value.0);
239        let sources_id = sources.ptr();
240        core::mem::forget(sources);
241        PtrRef::new(sources_id)
242    }
243}
244
245impl From<PlaylistEpisodeSource> for PtrRef {
246    fn from(value: PlaylistEpisodeSource) -> Self {
247        let description = optional_str_ptr(value.description);
248
249        let servers = ArrayRef::from(value.servers);
250        let servers_ptr = servers.ptr();
251        core::mem::forget(servers);
252
253        let source_ptr = unsafe {
254            create_episode_source(
255                value.id.as_ptr() as i32, 
256                value.id.len() as i32, 
257                value.display_name.as_ptr() as i32, 
258                value.display_name.len() as i32, 
259                description.0, 
260                description.1, 
261                servers_ptr
262            )
263        };
264        PtrRef::new(source_ptr)
265    }
266}
267
268impl From<PlaylistEpisodeServer> for PtrRef {
269    fn from(value: PlaylistEpisodeServer) -> Self {
270        let description = optional_str_ptr(value.description);
271        let server_ptr = unsafe {
272            create_episode_server(
273                value.id.as_ptr() as i32,
274                value.id.len() as i32,
275                value.display_name.as_ptr() as i32,
276                value.display_name.len() as i32,
277                description.0,
278                description.1
279            )
280        };
281        PtrRef::new(server_ptr)
282    }
283}
284
285impl From<PlaylistEpisodeServerSkipTime> for PtrRef {
286    fn from(value: PlaylistEpisodeServerSkipTime) -> Self {
287        PtrRef::new(
288            unsafe { 
289                create_episode_server_skip_time(
290                    value.start_time, 
291                    value.end_time, 
292                    value.skip_type
293                ) 
294            }
295        )
296    }
297}
298
299impl From<PlaylistEpisodeServerResponse> for PtrRef {
300    fn from(value: PlaylistEpisodeServerResponse) -> Self {
301        let links = ArrayRef::from(value.links);
302        let links_ptr = links.ptr();
303        core::mem::forget(links);
304
305        let subtitles = ArrayRef::from(value.subtitles);
306        let subtitles_ptr = subtitles.ptr();
307        core::mem::forget(subtitles);
308
309        let skip_times = ArrayRef::from(value.skip_times);
310        let skip_times_ptr = skip_times.ptr();
311        core::mem::forget(skip_times);
312
313        let mut headers = ObjectRef::new();
314        for header in value.headers {
315            headers.set(&header.key, header.value.into())
316        }
317        let headers_ptr = headers.ptr();
318        core::mem::forget(headers);
319
320        let response_ptr = unsafe {
321            create_episode_server_response(
322                links_ptr, 
323                subtitles_ptr,
324                skip_times_ptr,
325                headers_ptr
326            )
327        };
328        PtrRef::new(response_ptr)
329    }
330}
331
332impl From<PlaylistEpisodeServerLink> for PtrRef {
333    fn from(value: PlaylistEpisodeServerLink) -> Self {
334        let ptr = unsafe {
335            create_episode_server_link(
336                value.url.as_ptr() as i32, 
337                value.url.len() as i32,
338                value.quality.into(),
339                value.format_type as i32
340            )
341        };
342        PtrRef::new(ptr)
343    }
344}
345
346impl From<PlaylistEpisodeServerSubtitle> for PtrRef {
347    fn from(value: PlaylistEpisodeServerSubtitle) -> Self {
348        let ptr = unsafe {
349            create_episode_server_subtitle(
350                value.url.as_ptr() as i32,
351                value.url.len() as i32,
352                value.name.as_ptr() as i32,
353                value.name.len() as i32,
354                value.format as i32,
355                value.default,
356                value.autoselect
357            )
358        };
359        PtrRef::new(ptr)
360    }
361}