1extern crate alloc;
2
3use alloc::string::ToString;
4use alloc::vec;
5use alloc::{string::String, vec::Vec};
6
7use crate::imports::error::Result;
8use crate::imports::{PtrRef, ArrayRef};
9
10use super::conversion::optional_str_ptr;
11
12#[link(wasm_import_module = "structs_meta")]
13extern "C" {
14 fn create_search_filter_option(
15 option_id_ptr: i32,
16 option_id_len: i32,
17 name_ptr: i32,
18 name_len: i32
19 ) -> i32;
20
21 fn create_search_filter(
22 id_ptr: i32,
23 id_len: i32,
24 name_ptr: i32,
25 name_len: i32,
26 options_arr_ref: i32,
27 multiselect: bool,
28 required: bool
29 ) -> i32;
30
31 fn create_paging(
32 id_ptr: i32,
33 id_len: i32,
34 previous_page_ptr: i32,
35 previous_page_len: i32,
36 next_page_ptr: i32,
37 next_page_len: i32,
38 items_ptr: i32
39 ) -> i32;
40
41 fn create_discover_listing(
42 title_ptr: i32,
43 title_len: i32,
44 lising_type: i32,
45 paging_ptr: i32
46 ) -> i32;
47
48 fn create_playlist(
49 id_ptr: i32,
50 id_len: i32,
51 title_ptr: i32,
52 title_len: i32,
53 poster_image_ptr: i32,
54 poster_image_len: i32,
55 banner_image_ptr: i32,
56 banner_image_len: i32,
57 playlist_type: PlaylistType
58 ) -> i32;
59
60 fn create_playlist_details(
61 description_ptr: i32,
62 description_len: i32,
63 alternative_titles_ptr: i32,
64 alternative_posters_ptr: i32,
65 alternative_banners_ptr: i32,
66 genres_ptr: i32,
67 year_released: i32,
68 ratings: i32,
69 previews_ptr: i32
70 ) -> i32;
71
72 fn create_playlist_preview(
73 title_ptr: i32,
74 title_len: i32,
75 description_ptr: i32,
76 description_len: i32,
77 thumbnail_ptr: i32,
78 thumbnail_len: i32,
79 link_ptr: i32,
80 link_len: i32,
81 preview_type: PlaylistPreviewType
82 ) -> i32;
83
84 fn create_playlist_item(
85 id_ptr: i32,
86 id_len: i32,
87 title_ptr: i32,
88 title_len: i32,
89 description_ptr: i32,
90 description_len: i32,
91 thumbnail_ptr: i32,
92 thumbnail_len: i32,
93 number: f64,
94 timestamp_ptr: i32,
95 timestamp_len: i32,
96 tags_ptr: i32
97 ) -> i32;
98
99 fn create_playlist_items_response(
100 contents_ptr: i32,
101 all_groups_ptr: i32
102 ) -> i32;
103
104 fn create_playlist_group(
105 id: f64,
106 display_title_ptr: i32,
107 display_title_len: i32
108 ) -> i32;
109
110 fn create_playlist_group_page(
111 id_ptr: i32,
112 id_len: i32,
113 display_name_ptr: i32,
114 display_name_len: i32
115 ) -> i32;
116
117 fn create_playlist_group_items(
118 group_id: f64,
119 pagings_ptr: i32,
120 all_pages_ptr: i32
121 ) -> i32;
122}
123
124pub trait Meta {
125 fn search_filters() -> SearchFilters;
126 fn search(search_query: SearchQuery) -> Result<Paging<Playlist>>;
127 fn discover_listings() -> Result<DiscoverListings>;
128 fn playlist_details(id: String) -> Result<PlaylistDetails>;
129}
130
131pub type PlaylistID = String;
132
133#[derive(Debug, Clone)]
134pub struct Playlist {
135 pub id: PlaylistID,
136 pub title: Option<String>,
137 pub poster_image: Option<String>,
139 pub banner_image: Option<String>,
141 pub url: String,
143 pub status: PlaylistStatus,
145 pub playlist_type: PlaylistType
146}
147
148#[repr(C)]
149#[derive(PartialEq, Eq, Debug, Clone, Copy)]
150pub enum PlaylistStatus {
151 Unknown,
152 Upcoming,
153 Ongoing,
154 Completed,
155 Paused,
156 Cancelled,
157}
158
159#[repr(C)]
160#[derive(PartialEq, Eq, Debug, Clone, Copy)]
161pub enum PlaylistType {
162 Video,
163 Image,
164 Text
165}
166
167#[derive(Debug, Clone)]
168pub struct PlaylistDetails {
169 pub description: Option<String>,
170 pub alternative_titles: Vec<String>,
171 pub alternative_posters: Vec<String>,
173 pub alternative_banners: Vec<String>,
175 pub genres: Vec<String>,
176 pub year_released: Option<i32>,
177 pub ratings: Option<i32>,
179 pub previews: Vec<PlaylistPreview>
180}
181
182#[derive(Debug, Clone)]
183pub struct PlaylistPreview {
184 pub title: Option<String>,
185 pub description: Option<String>,
186 pub thumbnail: Option<String>,
187 pub link: String,
189 pub preview_type: PlaylistPreviewType
190}
191
192#[repr(C)]
193#[derive(PartialEq, Eq, Debug, Clone, Copy)]
194pub enum PlaylistPreviewType {
195 Video,
196 Image
197}
198
199pub type PlaylistItemID = String;
200
201#[derive(Debug, Clone, PartialEq, PartialOrd)]
202pub struct PlaylistItem {
203 pub id: PlaylistItemID,
204 pub title: Option<String>,
205 pub description: Option<String>,
206 pub thumbnail: Option<String>,
207 pub number: f64,
208 pub timestamp: Option<String>,
209 pub tags: Vec<String>,
210}
211
212#[derive(Debug, Clone)]
213pub struct PlaylistItemsRequest {
214 pub playlist_id: PlaylistID,
215 pub group_id: Option<f64>,
216 pub page_id: Option<String>,
217 pub item_id: Option<String>
218}
219
220#[derive(Debug, Clone)]
221pub struct PlaylistItemsResponse {
222 pub contents: Vec<PlaylistGroupContent>,
223 pub all_groups: Vec<PlaylistGroup>
224}
225
226#[derive(Debug, Clone)]
227pub struct PlaylistGroup {
228 pub id: f64,
229 pub display_title: Option<String>
230}
231
232#[derive(Debug, Clone)]
233pub struct PlaylistGroupPage {
234 pub id: String,
235 pub display_name: String
236}
237
238#[derive(Debug, Clone)]
239pub struct PlaylistGroupContent {
240 pub group_id: f64,
241 pub pagings: Vec<Paging<PlaylistItem>>,
242 pub all_pages: Vec<PlaylistGroupPage>
243}
244
245#[derive(Debug, Clone)]
246pub struct Paging<T> {
247 pub id: String,
248 pub previous_page: Option<String>,
249 pub next_page: Option<String>,
250 pub items: Vec<T>
251}
252
253#[derive(Debug, Clone)]
254pub struct DiscoverListing {
255 pub title: String,
256 pub listing_type: DiscoverListingType,
257 pub paging: Paging<Playlist>
258}
259
260#[repr(C)]
261#[derive(PartialEq, Eq, Debug, Clone, Copy)]
262pub enum DiscoverListingType {
263 Default,
264 Rank,
265 Featured
266}
267
268#[derive(Debug, Clone)]
269pub struct DiscoverListings(pub Vec<DiscoverListing>);
270
271#[derive(Debug, Clone)]
272pub struct SearchQuery {
273 pub query: String,
274 pub filters: Vec<SearchQueryFilter>,
275 pub page: Option<String>
276}
277
278#[derive(Debug, Clone)]
279pub struct SearchQueryFilter {
280 pub filter_id: String,
281 pub option_id: String
282}
283
284#[derive(Debug, Clone)]
285pub struct SearchFilters {
286 pub filters: Vec<SearchFilter>
287}
288
289#[derive(Debug, Clone)]
290pub struct SearchFilter {
291 pub filter_id: String,
292 pub display_name: String,
293 pub options: Vec<SearchFilterOption>,
294 pub multiselect: bool,
295 pub required: bool
296}
297
298#[derive(Debug, Clone)]
299pub struct SearchFilterOption {
300 pub option_id: String,
301 pub display_name: String
302}
303
304impl From<SearchFilters> for PtrRef {
305 fn from(value: SearchFilters) -> PtrRef {
306 let array_ref = ArrayRef::from(value.filters);
307 let filters_array_ref_ptr = array_ref.ptr();
308 core::mem::forget(array_ref);
309 Self::new(filters_array_ref_ptr)
310 }
311}
312
313impl From<SearchFilter> for PtrRef {
314 fn from(value: SearchFilter) -> Self {
315 let array_ref = ArrayRef::from(value.options);
316 let options_array_ref = array_ref.ptr();
317 core::mem::forget(array_ref);
318
319 let ptr = unsafe {
320 create_search_filter(
321 value.filter_id.as_ptr() as i32,
322 value.filter_id.len() as i32,
323 value.display_name.as_ptr() as i32,
324 value.display_name.len() as i32,
325 options_array_ref,
326 value.multiselect,
327 value.required
328 )
329 };
330 Self::new(ptr)
331 }
332}
333
334impl From<SearchFilterOption> for PtrRef {
335 fn from(value: SearchFilterOption) -> Self {
336 Self::new(
337 unsafe {
338 create_search_filter_option(
339 value.option_id.as_ptr() as i32,
340 value.option_id.len() as i32,
341 value.display_name.as_ptr() as i32,
342 value.display_name.len() as i32
343 )
344 }
345 )
346 }
347}
348
349impl Into<SearchQuery> for PtrRef {
350 fn into(self) -> SearchQuery {
351 if self.is_some() {
352 let reference = self.as_object();
353 if let Ok(search_query_ref) = reference {
354 let query = search_query_ref.get("query")
355 .as_string()
356 .unwrap_or_default();
357 let mut filters: Vec<SearchQueryFilter> = Vec::new();
358 if let Ok(filters_arr) = search_query_ref.get("filters").as_array() {
359 for item in filters_arr {
360 let filter_ref = match item.as_object() {
361 Ok(filter_ref) => filter_ref,
362 _ => continue,
363 };
364 let filter_id = match filter_ref.get("id").as_string() {
365 Ok(name) => name,
366 _ => continue,
367 };
368 let option_id = match filter_ref.get("optionId").as_string() {
369 Ok(value) => value,
370 _ => continue,
371 };
372 filters.push(
373 SearchQueryFilter {
374 filter_id,
375 option_id
376 }
377 )
378 }
379 }
380 let page = search_query_ref.get("page").as_string().ok();
381 return SearchQuery {
382 query,
383 filters,
384 page,
385 }
386 }
387 }
388 SearchQuery {
389 query: "".to_string(),
390 filters: vec![],
391 page: None,
392 }
393 }
394}
395
396impl<T> From<Paging<T>> for PtrRef where PtrRef: From<T> {
397 fn from(value: Paging<T>) -> Self {
398 let items = ArrayRef::from(value.items);
399 let items_ptr = items.ptr();
400 core::mem::forget(items);
401
402 let previous_page = optional_str_ptr(value.previous_page);
403 let next_page = optional_str_ptr(value.next_page);
404
405 let obj_ptr = unsafe {
406 create_paging(
407 value.id.as_ptr() as i32,
408 value.id.len() as i32,
409 previous_page.0,
410 previous_page.1,
411 next_page.0,
412 next_page.1,
413 items_ptr
414 )
415 };
416 Self::new(obj_ptr)
417 }
418}
419
420impl From<DiscoverListing> for PtrRef {
421 fn from(value: DiscoverListing) -> Self {
422 let title = value.title;
423 let listing_type = value.listing_type;
424 let paging: PtrRef = value.paging.into();
425 let paging_ptr = paging.pointer();
426 core::mem::forget(paging);
427
428 let obj_ptr = unsafe {
429 create_discover_listing(
430 title.as_ptr() as i32,
431 title.len() as i32,
432 listing_type as i32,
433 paging_ptr
434 )
435 };
436
437 Self::new(obj_ptr)
438 }
439}
440
441impl From<DiscoverListings> for PtrRef {
442 fn from(value: DiscoverListings) -> Self {
443 let array_ref = ArrayRef::from(value.0);
444 let array_ref_ptr = array_ref.ptr();
445 core::mem::forget(array_ref);
446 Self::new(array_ref_ptr)
447 }
448}
449
450impl From<Playlist> for PtrRef {
451 fn from(value: Playlist) -> Self {
452 let id = value.id;
453 let title = optional_str_ptr(value.title);
454 let poster_image = optional_str_ptr(value.poster_image);
455 let banner_image = optional_str_ptr(value.banner_image);
456 let host_ptr = unsafe {
457 create_playlist(
458 id.as_ptr() as i32,
459 id.len() as i32,
460 title.0,
461 title.1,
462 poster_image.0,
463 poster_image.1,
464 banner_image.0,
465 banner_image.1,
466 value.playlist_type
467 )
468 };
469 Self::new(host_ptr)
470 }
471}
472
473impl From<PlaylistDetails> for PtrRef {
474 fn from(value: PlaylistDetails) -> Self {
475 let description = optional_str_ptr(value.description);
476
477 let alternative_titles = ArrayRef::from(value.alternative_titles);
478 let alternative_titles_ptr = alternative_titles.ptr();
479 core::mem::forget(alternative_titles);
480
481 let alternative_posters = ArrayRef::from(value.alternative_posters);
482 let alternative_posters_ptr = alternative_posters.ptr();
483 core::mem::forget(alternative_posters);
484
485 let alternative_banners = ArrayRef::from(value.alternative_banners);
486 let alternative_banners_ptr = alternative_banners.ptr();
487 core::mem::forget(alternative_banners);
488
489 let genres = ArrayRef::from(value.genres);
490 let genres_ptr = genres.ptr();
491 core::mem::forget(genres);
492
493 let previews = ArrayRef::from(value.previews);
494 let previews_ptr = previews.ptr();
495 core::mem::forget(previews);
496
497 let host_ptr = unsafe {
498 create_playlist_details(
499 description.0,
500 description.1,
501 alternative_titles_ptr,
502 alternative_posters_ptr,
503 alternative_banners_ptr,
504 genres_ptr,
505 value.year_released.unwrap_or(-1),
506 value.ratings.unwrap_or(-1),
507 previews_ptr
508 )
509 };
510
511 Self::new(host_ptr)
512 }
513}
514
515impl From<PlaylistPreview> for PtrRef {
516 fn from(value: PlaylistPreview) -> Self {
517 let title = optional_str_ptr(value.title);
518 let description = optional_str_ptr(value.description);
519
520 let thumbnail = optional_str_ptr(value.thumbnail);
521
522 let host_ptr = unsafe {
523 create_playlist_preview(
524 title.0,
525 title.1,
526 description.0,
527 description.1,
528 thumbnail.0,
529 thumbnail.1,
530 value.link.as_ptr() as i32,
531 value.link.len() as i32,
532 value.preview_type
533 )
534 };
535 PtrRef::from(host_ptr)
536 }
537}
538
539impl From<PlaylistItem> for PtrRef {
540 fn from(value: PlaylistItem) -> Self {
541 let title = optional_str_ptr(value.title);
542 let description = optional_str_ptr(value.description);
543 let thumbnail = optional_str_ptr(value.thumbnail);
544 let timestamp = optional_str_ptr(value.timestamp);
545
546 let tags = ArrayRef::from(value.tags);
547 let tags_ptr = tags.ptr();
548 core::mem::forget(tags);
549
550 let host_ptr = unsafe {
551 create_playlist_item(
552 value.id.as_ptr() as i32,
553 value.id.len() as i32,
554 title.0,
555 title.1,
556 description.0,
557 description.1,
558 thumbnail.0,
559 thumbnail.1,
560 value.number,
561 timestamp.0,
562 timestamp.1,
563 tags_ptr
564 )
565 };
566 Self::from(host_ptr)
567 }
568}
569
570impl Into<PlaylistItemsRequest> for PtrRef {
571 fn into(self) -> PlaylistItemsRequest {
572 if let Ok(reference) = self.as_object() {
573 let playlist_id = reference.get("playlistId")
574 .as_string()
575 .unwrap_or_default();
576
577 let group_id = reference.get("groupId").as_float().ok();
578 let page_id = reference.get("pageId").as_string().ok();
579 let item_id = reference.get("itemId").as_string().ok();
580
581 PlaylistItemsRequest {
582 playlist_id,
583 group_id,
584 page_id,
585 item_id,
586 }
587 } else {
588 PlaylistItemsRequest {
589 playlist_id: "".to_string(),
590 group_id: None,
591 page_id: None,
592 item_id: None,
593 }
594 }
595 }
596}
597
598impl From<PlaylistItemsResponse> for PtrRef {
599 fn from(value: PlaylistItemsResponse) -> Self {
600 let contents = ArrayRef::from(value.contents);
601 let contents_ptr = contents.ptr();
602 core::mem::forget(contents);
603
604 let all_groups = ArrayRef::from(value.all_groups);
605 let all_groups_ptr = all_groups.ptr();
606 core::mem::forget(all_groups);
607
608 Self::new(unsafe { create_playlist_items_response(contents_ptr, all_groups_ptr) } )
609 }
610}
611
612impl From<PlaylistGroup> for PtrRef {
613 fn from(value: PlaylistGroup) -> Self {
614 let display_title = optional_str_ptr(value.display_title);
615 Self::new(
616 unsafe {
617 create_playlist_group(
618 value.id,
619 display_title.0,
620 display_title.1
621 )
622 }
623 )
624 }
625}
626
627impl From<PlaylistGroupPage> for PtrRef {
628 fn from(value: PlaylistGroupPage) -> Self {
629 Self::new(
630 unsafe {
631 create_playlist_group_page(
632 value.id.as_ptr() as i32,
633 value.id.len() as i32,
634 value.display_name.as_ptr() as i32,
635 value.display_name.len() as i32
636 )
637 }
638 )
639 }
640}
641
642impl From<PlaylistGroupContent> for PtrRef {
643 fn from(value: PlaylistGroupContent) -> Self {
644 let pagings = ArrayRef::from(value.pagings);
645 let pagings_ptr = pagings.ptr();
646 core::mem::forget(pagings);
647
648 let all_pages = ArrayRef::from(value.all_pages);
649 let all_pages_ptr = all_pages.ptr();
650 core::mem::forget(all_pages);
651
652 Self::new(
653 unsafe {
654 create_playlist_group_items(
655 value.group_id,
656 pagings_ptr,
657 all_pages_ptr
658 )
659 }
660 )
661 }
662}