1
1
use crate :: {
2
- JsonValue , UserId , WpApiParamOrder ,
2
+ UserId , WpApiParamOrder ,
3
3
date:: WpGmtDateTime ,
4
4
impl_as_query_value_for_new_type, impl_as_query_value_from_to_string,
5
5
posts:: {
@@ -11,6 +11,8 @@ use crate::{
11
11
} ,
12
12
} ;
13
13
use serde:: { Deserialize , Serialize } ;
14
+ use serde_json:: value:: RawValue ;
15
+ use std:: sync:: Arc ;
14
16
use std:: { collections:: HashMap , num:: ParseIntError , str:: FromStr } ;
15
17
use strum_macros:: IntoStaticStr ;
16
18
use wp_contextual:: WpContextual ;
@@ -527,7 +529,7 @@ pub struct SparseMedia {
527
529
#[ WpContext ( edit, embed, view) ]
528
530
pub mime_type : Option < String > ,
529
531
#[ WpContext ( edit, embed, view) ]
530
- pub media_details : Option < JsonValue > ,
532
+ pub media_details : Option < Arc < MediaDetails > > ,
531
533
#[ serde( rename = "post" ) ]
532
534
#[ WpContext ( edit, view) ]
533
535
#[ WpContextualOption ]
@@ -539,6 +541,107 @@ pub struct SparseMedia {
539
541
// meta field is omitted for now: https://github.com/Automattic/wordpress-rs/issues/381
540
542
}
541
543
544
+ #[ derive( Debug , Serialize , Deserialize , uniffi:: Object ) ]
545
+ #[ serde( transparent) ]
546
+ pub struct MediaDetails {
547
+ payload : Box < RawValue > ,
548
+ }
549
+
550
+ #[ uniffi:: export]
551
+ impl MediaDetails {
552
+ pub fn parse_as_mime_type ( & self , mime_type : String ) -> Option < MediaDetailsPayload > {
553
+ if mime_type. starts_with ( "image/" ) {
554
+ return serde_json:: from_str :: < ImageMediaDetails > ( self . payload . get ( ) )
555
+ . ok ( )
556
+ . map ( MediaDetailsPayload :: Image ) ;
557
+ } else if mime_type. starts_with ( "video/" ) || mime_type. starts_with ( "audio/" ) {
558
+ // Some audio files may be returned with a video MIME type, so we attempt to parse both.
559
+
560
+ if let Ok ( details) = serde_json:: from_str :: < VideoMediaDetails > ( self . payload . get ( ) ) {
561
+ return Some ( MediaDetailsPayload :: Video ( details) ) ;
562
+ }
563
+
564
+ if let Ok ( details) = serde_json:: from_str :: < AudioMediaDetails > ( self . payload . get ( ) ) {
565
+ return Some ( MediaDetailsPayload :: Audio ( details) ) ;
566
+ }
567
+ }
568
+
569
+ serde_json:: from_str :: < DocumentMediaDetails > ( self . payload . get ( ) )
570
+ . ok ( )
571
+ . map ( MediaDetailsPayload :: Document )
572
+ }
573
+ }
574
+
575
+ #[ derive( Debug , uniffi:: Enum ) ]
576
+ pub enum MediaDetailsPayload {
577
+ Audio ( AudioMediaDetails ) ,
578
+ Image ( ImageMediaDetails ) ,
579
+ Video ( VideoMediaDetails ) ,
580
+ Document ( DocumentMediaDetails ) ,
581
+ }
582
+
583
+ #[ derive( Debug , Serialize , Deserialize , uniffi:: Record ) ]
584
+ pub struct AudioMediaDetails {
585
+ #[ serde( rename = "filesize" ) ]
586
+ pub file_size : u64 ,
587
+ pub length : u64 ,
588
+ pub length_formatted : String ,
589
+
590
+ #[ serde( rename = "dataformat" ) ]
591
+ pub data_format : Option < String > ,
592
+ pub codec : Option < String > ,
593
+ pub sample_rate : Option < u32 > ,
594
+ pub channels : Option < u8 > ,
595
+ pub bits_per_sample : Option < u8 > ,
596
+ pub lossless : Option < bool > ,
597
+ #[ serde( rename = "channelmode" ) ]
598
+ pub channel_mode : Option < String > ,
599
+ pub bitrate : Option < f64 > ,
600
+ pub compression_ratio : Option < f64 > ,
601
+ #[ serde( rename = "fileformat" ) ]
602
+ pub file_format : Option < String > ,
603
+ }
604
+
605
+ #[ derive( Debug , Serialize , Deserialize , uniffi:: Record ) ]
606
+ pub struct ImageMediaDetails {
607
+ #[ serde( rename = "filesize" ) ]
608
+ pub file_size : u64 ,
609
+
610
+ pub width : u32 ,
611
+ pub height : u32 ,
612
+ pub file : String ,
613
+ pub sizes : Option < HashMap < String , ScaledImageDetails > > ,
614
+ }
615
+
616
+ #[ derive( Debug , Serialize , Deserialize , uniffi:: Record ) ]
617
+ pub struct ScaledImageDetails {
618
+ pub file : String ,
619
+ pub width : u32 ,
620
+ pub height : u32 ,
621
+ pub source_url : String ,
622
+ }
623
+
624
+ #[ derive( Debug , Serialize , Deserialize , uniffi:: Record ) ]
625
+ pub struct VideoMediaDetails {
626
+ #[ serde( rename = "filesize" ) ]
627
+ pub file_size : u64 ,
628
+ pub length : u32 ,
629
+ pub width : u32 ,
630
+ pub height : u32 ,
631
+
632
+ #[ serde( rename = "fileformat" ) ]
633
+ pub file_format : Option < String > ,
634
+ #[ serde( rename = "dataformat" ) ]
635
+ pub data_format : Option < String > ,
636
+ pub created_timestamp : Option < u64 > ,
637
+ }
638
+
639
+ #[ derive( Debug , Serialize , Deserialize , uniffi:: Record ) ]
640
+ pub struct DocumentMediaDetails {
641
+ #[ serde( rename = "filesize" ) ]
642
+ pub file_size : u64 ,
643
+ }
644
+
542
645
#[ derive( Debug , Serialize , Deserialize , uniffi:: Record , WpContextual ) ]
543
646
pub struct SparseMediaDescription {
544
647
#[ WpContext ( edit) ]
@@ -650,4 +753,60 @@ mod tests {
650
753
"page=11&per_page=22&search=s_q&{after}&{modified_after}&author=111%2C112&author_exclude=211%2C212&{before}&{modified_before}&exclude=1111%2C1112&include=2111%2C2112&offset=11111&order=desc&orderby=slug&parent=44444%2C44445&search_columns=post_content%2Cpost_excerpt&slug=sl_1%2Csl_2&status=inherit%2Cprivate%2Ctrash&parent_exclude=55555%2C55556&media_type=image&mime_type=image%2Fjpeg"
651
754
)
652
755
}
756
+
757
+ #[ test]
758
+ fn media_details_round_trip ( ) {
759
+ let original = r#"
760
+ {
761
+ "id": 11,
762
+ "date": "2025-05-29T03:15:55",
763
+ "date_gmt": "2025-05-29T03:15:55",
764
+ "guid": { "rendered": "https://example.com/dummy.docx" },
765
+ "modified": "2025-05-29T03:15:55",
766
+ "modified_gmt": "2025-05-29T03:15:55",
767
+ "slug": "dummy-slug",
768
+ "status": "dummy-status",
769
+ "type": "dummy-type",
770
+ "link": "https://example.com/dummy-link/",
771
+ "title": { "rendered": "Dummy Title" },
772
+ "author": 1,
773
+ "featured_media": 0,
774
+ "comment_status": "dummy-comment-status",
775
+ "ping_status": "dummy-ping-status",
776
+ "template": "dummy-template",
777
+ "meta": [],
778
+ "class_list": [
779
+ "dummy-class-1",
780
+ "dummy-class-2",
781
+ "dummy-class-3",
782
+ "dummy-class-4",
783
+ "dummy-class-5"
784
+ ],
785
+ "description": {
786
+ "rendered": "<p class=\"dummy-class\"><a href='https://example.com/dummy.docx'>Dummy Link</a></p>\n"
787
+ },
788
+ "caption": {
789
+ "rendered": "<p>Dummy Caption</p>\n"
790
+ },
791
+ "alt_text": "Dummy Alt Text",
792
+ "media_type": "dummy-media-type",
793
+ "mime_type": "dummy/mime-type",
794
+ "media_details": {
795
+ "filesize": 7378,
796
+ "sizes": {}
797
+ },
798
+ "post": null,
799
+ "source_url": "https://example.com/dummy.docx"
800
+ }
801
+ "# ;
802
+
803
+ let media = serde_json:: from_str :: < MediaWithViewContext > ( original) . unwrap ( ) ;
804
+ let serialized = serde_json:: to_string ( & media) . unwrap ( ) ;
805
+ let json = serde_json:: from_str :: < serde_json:: Value > ( serialized. as_str ( ) ) . unwrap ( ) ;
806
+ assert_eq ! ( json[ "media_details" ] [ "filesize" ] , 7378 ) ;
807
+ assert_eq ! (
808
+ json[ "media_details" ] [ "sizes" ] ,
809
+ serde_json:: Value :: Object ( serde_json:: Map :: new( ) )
810
+ ) ;
811
+ }
653
812
}
0 commit comments