1use iri_string::types::{IriAbsoluteString, IriString};
2use serde::{Deserialize, Serialize};
3
4mod oembed;
5
6use crate::models::media::oembed::OEmbed;
7
8#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
9pub struct MediaEmbed {
10 content: String,
11 width: u16,
12 scrolling: bool,
13 media_domain_url: Option<IriString>,
14 height: u16,
15}
16
17#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
18#[serde(untagged)]
19pub enum Media {
20 OEmbed(OEmbed),
21 RedditVideo { reddit_video: RedditVideo },
22}
23
24#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
25#[serde(rename_all = "snake_case")]
26pub struct RedditVideo {
27 pub bitrate_kbps: u32,
28 pub fallback_url: IriAbsoluteString,
29 pub height: u32,
30 pub width: u32,
31 pub scrubber_media_url: IriAbsoluteString,
32 pub dash_url: IriAbsoluteString,
33 pub duration: u32,
34 pub hls_url: IriAbsoluteString,
35 pub is_gif: bool,
36 pub transcoding_status: TranscodingStatus,
37}
38
39#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
40pub struct MediaThumbnail {
41 #[serde(rename = "thumbnail_width")]
42 width: u16,
43 #[serde(rename = "thumbnail_height")]
44 height: u16,
45 #[serde(rename = "thumbnail_url")]
46 url: IriAbsoluteString,
47}
48
49#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
50#[serde(rename_all = "snake_case")]
51pub enum TranscodingStatus {
52 Completed,
53}
54
55#[cfg(test)]
56mod test_media {
57 #![allow(clippy::pedantic)]
58
59 use super::*;
60 use crate::models::media::oembed::{
61 streamable, OEmbedData, OEmbedProvider, OEmbedType, OEmbedVersion,
62 };
63
64 #[test]
65 fn test_youtube() {
66 let media = Media::OEmbed(OEmbed::YouTube(OEmbedData {
67 height: Default::default(),
68 width: Default::default(),
69 html: Default::default(),
70 provider: OEmbedProvider {
71 name: Default::default(),
72 url: "https://www.reddit.com/".parse().unwrap(),
73 },
74 version: OEmbedVersion::V1_0,
75 thumbnail: Some(MediaThumbnail {
76 width: Default::default(),
77 url: "https://www.reddit.com/".parse().unwrap(),
78 height: Default::default(),
79 }),
80 ty: OEmbedType::Video,
81 vendor_specific_data: oembed::youtube::YouTube {
82 title: Default::default(),
83 author_name: Default::default(),
84 author_url: "https://www.reddit.com/".parse().unwrap(),
85 },
86 }));
87
88 let _json = serde_json::to_string_pretty(&media).unwrap();
89 }
90
91 #[test]
92 fn test_streamable() {
93 let media = Media::OEmbed(OEmbed::Streamable(OEmbedData {
94 height: Default::default(),
95 width: Default::default(),
96 html: Default::default(),
97 provider: OEmbedProvider {
98 name: Default::default(),
99 url: "https://www.reddit.com/".parse().unwrap(),
100 },
101 version: OEmbedVersion::V1_0,
102 thumbnail: Some(MediaThumbnail {
103 width: Default::default(),
104 url: "https://www.reddit.com/".parse().unwrap(),
105 height: Default::default(),
106 }),
107 ty: OEmbedType::Video,
108 vendor_specific_data: streamable::Streamable {
109 title: Default::default(),
110 description: Default::default(),
111 },
112 }));
113
114 let json = serde_json::to_string_pretty(&media).unwrap();
115
116 let json = r#"
119 {
120 "oembed": {
121 "provider_url": "https://streamable.com",
122 "description": "Watch this video on Streamable.",
123 "title": "Streamable Video",
124 "thumbnail_width": 1280,
125 "height": 338,
126 "width": 600,
127 "html": "<iframe class=\"embedly-embed\" src=\"https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fstreamable.com%2Fo%2Fgixxf0&display_name=Streamable&url=https%3A%2F%2Fstreamable.com%2Fgixxf0&image=https%3A%2F%2Fcdn-cf-east.streamable.com%2Fimage%2Fgixxf0.jpg%3FExpires%3D1634216820%26Signature%3DdoMgebEgi-eHwDrMcGIUyMTfOSBbA5tjLo9PtT4D4tjPPL3oLGzOGEEaro3kGUhP6L-0PplxBbHiuNBoFGDiKMJv0KWMBQ4VEdLSIQ0wB78%7EPe4kYENxS3gDtYk%7EUjUktnu3fFLeTCLCYTFVNm7e1mEqpIrB0yB6b9RAQ9igkaa2jhpRHceh0yOvQgXe-a-yg4VaDMPHHE4XAYHwzhO68s7066cWtvgj-Uci1PA%7E5W5qt7FqFDPT3FAmLgGbKopfQq-y8mKfTHu7KOfWVQgwEuEnHGe9I10cVW7e%7EMyfO9PY5oDzczhH7ffGU1fnDRXTQ2GGjhdkV2joVOgQ4CuX2Q__%26Key-Pair-Id%3DAPKAIEYUVEN4EVB2OKEQ&key=ed8fa8699ce04833838e66ce79ba05f1&type=text%2Fhtml&schema=streamable\" width=\"600\" height=\"338\" scrolling=\"no\" title=\"Streamable embed\" frameborder=\"0\" allow=\"autoplay; fullscreen\" allowfullscreen=\"true\"></iframe>",
128 "version": "1.0",
129 "provider_name": "Streamable",
130 "thumbnail_url": "https://cdn-cf-east.streamable.com/image/gixxf0.jpg?Expires=1634216820&Signature=doMgebEgi-eHwDrMcGIUyMTfOSBbA5tjLo9PtT4D4tjPPL3oLGzOGEEaro3kGUhP6L-0PplxBbHiuNBoFGDiKMJv0KWMBQ4VEdLSIQ0wB78~Pe4kYENxS3gDtYk~UjUktnu3fFLeTCLCYTFVNm7e1mEqpIrB0yB6b9RAQ9igkaa2jhpRHceh0yOvQgXe-a-yg4VaDMPHHE4XAYHwzhO68s7066cWtvgj-Uci1PA~5W5qt7FqFDPT3FAmLgGbKopfQq-y8mKfTHu7KOfWVQgwEuEnHGe9I10cVW7e~MyfO9PY5oDzczhH7ffGU1fnDRXTQ2GGjhdkV2joVOgQ4CuX2Q__&Key-Pair-Id=APKAIEYUVEN4EVB2OKEQ",
131 "type": "video",
132 "thumbnail_height": 720
133 },
134 "type": "streamable.com"
135 }"#;
136
137 dbg!(serde_json::from_str::<Media>(json).unwrap());
138 }
139
140 #[test]
141 fn test_twitter() {
142 let json = r##"
143 {
144 "type": "twitter.com",
145 "oembed": {
146 "version": "1.0",
147 "url": "https://twitter.com/LordKebun/status/1447420243010281473",
148 "author_name": "Kebun",
149 "height": null,
150 "width": 350,
151 "html": "\u003Cblockquote class=\"twitter-video\"\u003E\u003Cp lang=\"en\" dir=\"ltr\"\u003ERate my Ramee RP Impersonation. \u003Ca href=\"https://t.co/vipSaqsrDe\"\u003Epic.twitter.com/vipSaqsrDe\u003C/a\u003E\u003C/p\u003E\u0026mdash; Kebun (@LordKebun) \u003Ca href=\"https://twitter.com/LordKebun/status/1447420243010281473?ref_src=twsrc%5Etfw\"\u003EOctober 11, 2021\u003C/a\u003E\u003C/blockquote\u003E\n\u003Cscript async src=\"https://platform.twitter.com/widgets.js\" charset=\"utf-8\"\u003E\u003C/script\u003E\n",
152 "author_url": "https://twitter.com/LordKebun",
153 "cache_age": 3153600000,
154 "type": "rich",
155 "provider_url": "https://twitter.com",
156 "provider_name": "Twitter"
157 }
158 }"##;
159
160 dbg!(serde_json::from_str::<Media>(json).unwrap());
161 }
162
163 #[test]
164 fn test_oembed_bandcamp() {
165 let json = r##"{
166 "type": "futurespiritualman.bandcamp.com",
167 "oembed": {
168 "provider_url": "http://bandcamp.com",
169 "description": "beaf by futurespiritualman, released 01 January 2021 1. b-horn ft._Lil Peter_&_Frank.mp3 2. funk_part_2._ft_FUNKEYMAN_supreme.mp3 3. the_toop_ft._Lil_Peter.mp3 4. i_eat_rocks.mp3 5. dog_walker_ft._Gizzer_NP_.mp3 6. beefstew_ft._SuizideDeth.mp3 7. going_to_jail_part_II.mp3 8. breats_ft._Lil_Peter.mp3 9. my_truck_ft._BigK2000.mp3 10. balls2man.mp3 11. FIVE_thefourtyninerswinin2075.mp3 12. good_he.mp3 13. i_eat_rocks.mp3 14. truck_(_truck_truck_truck_truck_).mp3 15. my_truck_part_2._ft._Frank(version_1_).mp3 16. cum)lord__.mp3 17. joe.mp3 18. truck_4_(version2).mp3 19. i_love_youft.__Frank.mp3 20. RAP_ft._thelegendsupreme.mp3 21.",
170 "title": "beaf, by futurespiritualman",
171 "type": "rich",
172 "thumbnail_width": 700,
173 "height": 467,
174 "width": 350,
175 "html": "<iframe class=\"embedly-embed\" src=\"https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fbandcamp.com%2FEmbeddedPlayer%2Fv%3D2%2Falbum%3D1766047770%2Fsize%3Dlarge%2Flinkcol%3D0084B4%2Fnotracklist%3Dtrue%2Ftwittercard%3Dtrue%2F&display_name=BandCamp&url=https%3A%2F%2Ffuturespiritualman.bandcamp.com%2Falbum%2Fbeaf&image=https%3A%2F%2Ff4.bcbits.com%2Fimg%2Fa2778328046_5.jpg&key=ed8fa8699ce04833838e66ce79ba05f1&type=text%2Fhtml&schema=bandcamp\" width=\"350\" height=\"467\" scrolling=\"no\" title=\"BandCamp embed\" frameborder=\"0\" allow=\"autoplay; fullscreen\" allowfullscreen=\"true\"></iframe>",
176 "version": "1.0",
177 "provider_name": "BandCamp",
178 "thumbnail_url": "https://f4.bcbits.com/img/a2778328046_5.jpg",
179 "thumbnail_height": 700
180 }
181 }"##;
182
183 dbg!(serde_json::from_str::<Media>(json).unwrap());
184 }
185}