common: Add support for extensible video events
This commit is contained in:
parent
2d88df7fb7
commit
57e8b77d87
@ -35,6 +35,7 @@ unstable-msc2676 = []
|
||||
unstable-msc2677 = []
|
||||
unstable-msc3551 = ["unstable-msc1767"]
|
||||
unstable-msc3552 = ["unstable-msc1767", "unstable-msc3551"]
|
||||
unstable-msc3553 = ["unstable-msc3552"]
|
||||
|
||||
[dependencies]
|
||||
base64 = "0.13.0"
|
||||
|
@ -179,6 +179,8 @@ pub mod space;
|
||||
pub mod sticker;
|
||||
pub mod tag;
|
||||
pub mod typing;
|
||||
#[cfg(feature = "unstable-msc3553")]
|
||||
pub mod video;
|
||||
|
||||
#[cfg(feature = "unstable-msc2675")]
|
||||
pub use self::relation::Relations;
|
||||
|
@ -61,6 +61,8 @@ event_enum! {
|
||||
"m.room.message.feedback",
|
||||
"m.room.redaction",
|
||||
"m.sticker",
|
||||
#[cfg(feature = "unstable-msc3553")]
|
||||
"m.video",
|
||||
}
|
||||
|
||||
/// Any state event.
|
||||
@ -372,6 +374,8 @@ impl AnyMessageLikeEventContent {
|
||||
Self::File(ev) => ev.relates_to.clone().map(Into::into),
|
||||
#[cfg(feature = "unstable-msc3552")]
|
||||
Self::Image(ev) => ev.relates_to.clone().map(Into::into),
|
||||
#[cfg(feature = "unstable-msc3553")]
|
||||
Self::Video(ev) => ev.relates_to.clone().map(Into::into),
|
||||
Self::CallAnswer(_)
|
||||
| Self::CallInvite(_)
|
||||
| Self::CallHangup(_)
|
||||
|
108
crates/ruma-common/src/events/video.rs
Normal file
108
crates/ruma-common/src/events/video.rs
Normal file
@ -0,0 +1,108 @@
|
||||
//! Types for extensible video message events ([MSC3553]).
|
||||
//!
|
||||
//! [MSC3553]: https://github.com/matrix-org/matrix-spec-proposals/pull/3553
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
use js_int::UInt;
|
||||
use ruma_macros::EventContent;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::{
|
||||
file::FileContent,
|
||||
image::{Captions, Thumbnails},
|
||||
message::MessageContent,
|
||||
room::message::Relation,
|
||||
};
|
||||
|
||||
/// The payload for an extensible video message.
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, EventContent)]
|
||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||
#[ruma_event(type = "m.video", kind = MessageLike)]
|
||||
pub struct VideoEventContent {
|
||||
/// The text representation of the message.
|
||||
#[serde(flatten)]
|
||||
pub message: MessageContent,
|
||||
|
||||
/// The file content of the message.
|
||||
#[serde(rename = "org.matrix.msc1767.file")]
|
||||
pub file: FileContent,
|
||||
|
||||
/// The video content of the message.
|
||||
#[serde(rename = "org.matrix.msc1767.video")]
|
||||
pub video: Box<VideoContent>,
|
||||
|
||||
/// The thumbnails of the message.
|
||||
#[serde(
|
||||
rename = "org.matrix.msc1767.thumbnail",
|
||||
default,
|
||||
skip_serializing_if = "Thumbnails::is_empty"
|
||||
)]
|
||||
pub thumbnail: Thumbnails,
|
||||
|
||||
/// The captions of the message.
|
||||
#[serde(
|
||||
rename = "org.matrix.msc1767.caption",
|
||||
default,
|
||||
skip_serializing_if = "Captions::is_empty"
|
||||
)]
|
||||
pub caption: Captions,
|
||||
|
||||
/// Information about related messages.
|
||||
#[serde(flatten, skip_serializing_if = "Option::is_none")]
|
||||
pub relates_to: Option<Relation>,
|
||||
}
|
||||
|
||||
impl VideoEventContent {
|
||||
/// Creates a new `VideoEventContent` with the given plain text message and file.
|
||||
pub fn plain(message: impl Into<String>, file: FileContent) -> Self {
|
||||
Self {
|
||||
message: MessageContent::plain(message),
|
||||
file,
|
||||
video: Default::default(),
|
||||
thumbnail: Default::default(),
|
||||
caption: Default::default(),
|
||||
relates_to: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new `VideoEventContent` with the given message and file.
|
||||
pub fn with_message(message: MessageContent, file: FileContent) -> Self {
|
||||
Self {
|
||||
message,
|
||||
file,
|
||||
video: Default::default(),
|
||||
thumbnail: Default::default(),
|
||||
caption: Default::default(),
|
||||
relates_to: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Video content.
|
||||
#[derive(Default, Clone, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||
pub struct VideoContent {
|
||||
/// The height of the video in pixels.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub height: Option<UInt>,
|
||||
|
||||
/// The width of the video in pixels.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub width: Option<UInt>,
|
||||
|
||||
/// The duration of the video in milliseconds.
|
||||
#[serde(
|
||||
with = "ruma_common::serde::duration::opt_ms",
|
||||
default,
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
pub duration: Option<Duration>,
|
||||
}
|
||||
|
||||
impl VideoContent {
|
||||
/// Creates a new empty `VideoContent`.
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
}
|
@ -19,3 +19,4 @@ mod room_message;
|
||||
mod state_event;
|
||||
mod stripped;
|
||||
mod to_device;
|
||||
mod video;
|
||||
|
335
crates/ruma-common/tests/events/video.rs
Normal file
335
crates/ruma-common/tests/events/video.rs
Normal file
@ -0,0 +1,335 @@
|
||||
#![cfg(feature = "unstable-msc3553")]
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
use assign::assign;
|
||||
use js_int::uint;
|
||||
use matches::assert_matches;
|
||||
use ruma_common::{
|
||||
event_id,
|
||||
events::{
|
||||
file::{EncryptedContentInit, FileContent, FileContentInfo},
|
||||
image::{
|
||||
Captions, ThumbnailContent, ThumbnailFileContent, ThumbnailFileContentInfo, Thumbnails,
|
||||
},
|
||||
message::MessageContent,
|
||||
room::{
|
||||
message::{InReplyTo, Relation},
|
||||
JsonWebKeyInit,
|
||||
},
|
||||
video::{VideoContent, VideoEventContent},
|
||||
AnyMessageLikeEvent, MessageLikeEvent, Unsigned,
|
||||
},
|
||||
mxc_uri, room_id,
|
||||
serde::Base64,
|
||||
user_id, MilliSecondsSinceUnixEpoch,
|
||||
};
|
||||
use serde_json::{from_value as from_json_value, json, to_value as to_json_value};
|
||||
|
||||
#[test]
|
||||
fn plain_content_serialization() {
|
||||
let event_content = VideoEventContent::plain(
|
||||
"Upload: my_video.webm",
|
||||
FileContent::plain(mxc_uri!("mxc://notareal.hs/abcdef").to_owned(), None),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
to_json_value(&event_content).unwrap(),
|
||||
json!({
|
||||
"org.matrix.msc1767.text": "Upload: my_video.webm",
|
||||
"org.matrix.msc1767.file": {
|
||||
"url": "mxc://notareal.hs/abcdef",
|
||||
},
|
||||
"org.matrix.msc1767.video": {}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn encrypted_content_serialization() {
|
||||
let event_content = VideoEventContent::plain(
|
||||
"Upload: my_video.webm",
|
||||
FileContent::encrypted(
|
||||
mxc_uri!("mxc://notareal.hs/abcdef").to_owned(),
|
||||
EncryptedContentInit {
|
||||
key: JsonWebKeyInit {
|
||||
kty: "oct".to_owned(),
|
||||
key_ops: vec!["encrypt".to_owned(), "decrypt".to_owned()],
|
||||
alg: "A256CTR".to_owned(),
|
||||
k: Base64::parse("TLlG_OpX807zzQuuwv4QZGJ21_u7weemFGYJFszMn9A").unwrap(),
|
||||
ext: true,
|
||||
}
|
||||
.into(),
|
||||
iv: Base64::parse("S22dq3NAX8wAAAAAAAAAAA").unwrap(),
|
||||
hashes: [(
|
||||
"sha256".to_owned(),
|
||||
Base64::parse("aWOHudBnDkJ9IwaR1Nd8XKoI7DOrqDTwt6xDPfVGN6Q").unwrap(),
|
||||
)]
|
||||
.into(),
|
||||
v: "v2".to_owned(),
|
||||
}
|
||||
.into(),
|
||||
None,
|
||||
),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
to_json_value(&event_content).unwrap(),
|
||||
json!({
|
||||
"org.matrix.msc1767.text": "Upload: my_video.webm",
|
||||
"org.matrix.msc1767.file": {
|
||||
"url": "mxc://notareal.hs/abcdef",
|
||||
"key": {
|
||||
"kty": "oct",
|
||||
"key_ops": ["encrypt", "decrypt"],
|
||||
"alg": "A256CTR",
|
||||
"k": "TLlG_OpX807zzQuuwv4QZGJ21_u7weemFGYJFszMn9A",
|
||||
"ext": true
|
||||
},
|
||||
"iv": "S22dq3NAX8wAAAAAAAAAAA",
|
||||
"hashes": {
|
||||
"sha256": "aWOHudBnDkJ9IwaR1Nd8XKoI7DOrqDTwt6xDPfVGN6Q"
|
||||
},
|
||||
"v": "v2"
|
||||
},
|
||||
"org.matrix.msc1767.video": {}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn event_serialization() {
|
||||
let event = MessageLikeEvent {
|
||||
content: assign!(
|
||||
VideoEventContent::with_message(
|
||||
MessageContent::html(
|
||||
"Upload: my_lava_lamp.webm",
|
||||
"Upload: <strong>my_lava_lamp.webm</strong>",
|
||||
),
|
||||
FileContent::plain(
|
||||
mxc_uri!("mxc://notareal.hs/abcdef").to_owned(),
|
||||
Some(Box::new(assign!(
|
||||
FileContentInfo::new(),
|
||||
{
|
||||
name: Some("my_lava_lamp.webm".to_owned()),
|
||||
mimetype: Some("video/webm".to_owned()),
|
||||
size: Some(uint!(1_897_774)),
|
||||
}
|
||||
))),
|
||||
)
|
||||
),
|
||||
{
|
||||
video: Box::new(assign!(
|
||||
VideoContent::new(),
|
||||
{
|
||||
width: Some(uint!(1920)),
|
||||
height: Some(uint!(1080)),
|
||||
duration: Some(Duration::from_secs(15)),
|
||||
}
|
||||
)),
|
||||
thumbnail: Thumbnails::new(&[ThumbnailContent::new(
|
||||
ThumbnailFileContent::plain(
|
||||
mxc_uri!("mxc://notareal.hs/thumbnail").to_owned(),
|
||||
Some(Box::new(assign!(ThumbnailFileContentInfo::new(), {
|
||||
mimetype: Some("image/jpeg".to_owned()),
|
||||
size: Some(uint!(334_593)),
|
||||
})))
|
||||
),
|
||||
None
|
||||
)]),
|
||||
caption: Captions::plain("This is my awesome vintage lava lamp"),
|
||||
relates_to: Some(Relation::Reply {
|
||||
in_reply_to: InReplyTo::new(event_id!("$replyevent:example.com").to_owned()),
|
||||
}),
|
||||
}
|
||||
),
|
||||
event_id: event_id!("$event:notareal.hs").to_owned(),
|
||||
sender: user_id!("@user:notareal.hs").to_owned(),
|
||||
origin_server_ts: MilliSecondsSinceUnixEpoch(uint!(134_829_848)),
|
||||
room_id: room_id!("!roomid:notareal.hs").to_owned(),
|
||||
unsigned: Unsigned::default(),
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
to_json_value(&event).unwrap(),
|
||||
json!({
|
||||
"content": {
|
||||
"org.matrix.msc1767.message": [
|
||||
{ "body": "Upload: <strong>my_lava_lamp.webm</strong>", "mimetype": "text/html"},
|
||||
{ "body": "Upload: my_lava_lamp.webm", "mimetype": "text/plain"},
|
||||
],
|
||||
"org.matrix.msc1767.file": {
|
||||
"url": "mxc://notareal.hs/abcdef",
|
||||
"name": "my_lava_lamp.webm",
|
||||
"mimetype": "video/webm",
|
||||
"size": 1_897_774,
|
||||
},
|
||||
"org.matrix.msc1767.video": {
|
||||
"width": 1920,
|
||||
"height": 1080,
|
||||
"duration": 15_000,
|
||||
},
|
||||
"org.matrix.msc1767.thumbnail": [
|
||||
{
|
||||
"url": "mxc://notareal.hs/thumbnail",
|
||||
"mimetype": "image/jpeg",
|
||||
"size": 334_593,
|
||||
}
|
||||
],
|
||||
"org.matrix.msc1767.caption": [
|
||||
{
|
||||
"body": "This is my awesome vintage lava lamp",
|
||||
"mimetype": "text/plain",
|
||||
}
|
||||
],
|
||||
"m.relates_to": {
|
||||
"m.in_reply_to": {
|
||||
"event_id": "$replyevent:example.com"
|
||||
}
|
||||
}
|
||||
},
|
||||
"event_id": "$event:notareal.hs",
|
||||
"origin_server_ts": 134_829_848,
|
||||
"room_id": "!roomid:notareal.hs",
|
||||
"sender": "@user:notareal.hs",
|
||||
"type": "m.video",
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn plain_content_deserialization() {
|
||||
let json_data = json!({
|
||||
"org.matrix.msc1767.text": "Video: my_cat.mp4",
|
||||
"org.matrix.msc1767.file": {
|
||||
"url": "mxc://notareal.hs/abcdef",
|
||||
},
|
||||
"org.matrix.msc1767.video": {
|
||||
"duration": 5_668,
|
||||
},
|
||||
"org.matrix.msc1767.caption": [
|
||||
{
|
||||
"body": "Look at my cat!",
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
assert_matches!(
|
||||
from_json_value::<VideoEventContent>(json_data)
|
||||
.unwrap(),
|
||||
VideoEventContent { message, file, video, thumbnail, caption, .. }
|
||||
if message.find_plain() == Some("Video: my_cat.mp4")
|
||||
&& message.find_html().is_none()
|
||||
&& file.url == "mxc://notareal.hs/abcdef"
|
||||
&& video.width.is_none()
|
||||
&& video.height.is_none()
|
||||
&& video.duration == Some(Duration::from_millis(5_668))
|
||||
&& thumbnail.is_empty()
|
||||
&& caption.find_plain() == Some("Look at my cat!")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn encrypted_content_deserialization() {
|
||||
let json_data = json!({
|
||||
"org.matrix.msc1767.text": "Upload: my_cat.mp4",
|
||||
"org.matrix.msc1767.file": {
|
||||
"url": "mxc://notareal.hs/abcdef",
|
||||
"key": {
|
||||
"kty": "oct",
|
||||
"key_ops": ["encrypt", "decrypt"],
|
||||
"alg": "A256CTR",
|
||||
"k": "TLlG_OpX807zzQuuwv4QZGJ21_u7weemFGYJFszMn9A",
|
||||
"ext": true
|
||||
},
|
||||
"iv": "S22dq3NAX8wAAAAAAAAAAA",
|
||||
"hashes": {
|
||||
"sha256": "aWOHudBnDkJ9IwaR1Nd8XKoI7DOrqDTwt6xDPfVGN6Q"
|
||||
},
|
||||
"v": "v2"
|
||||
},
|
||||
"org.matrix.msc1767.video": {},
|
||||
"org.matrix.msc1767.thumbnail": [
|
||||
{
|
||||
"url": "mxc://notareal.hs/thumbnail",
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
assert_matches!(
|
||||
from_json_value::<VideoEventContent>(json_data)
|
||||
.unwrap(),
|
||||
VideoEventContent { message, file, video, thumbnail, caption, .. }
|
||||
if message.find_plain() == Some("Upload: my_cat.mp4")
|
||||
&& message.find_html().is_none()
|
||||
&& file.url == "mxc://notareal.hs/abcdef"
|
||||
&& file.encryption_info.is_some()
|
||||
&& video.width.is_none()
|
||||
&& video.height.is_none()
|
||||
&& video.duration.is_none()
|
||||
&& thumbnail.thumbnails()[0].file.url == "mxc://notareal.hs/thumbnail"
|
||||
&& caption.is_empty()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn message_event_deserialization() {
|
||||
let json_data = json!({
|
||||
"content": {
|
||||
"org.matrix.msc1767.text": "Upload: my_gnome.webm",
|
||||
"org.matrix.msc1767.file": {
|
||||
"url": "mxc://notareal.hs/abcdef",
|
||||
"name": "my_gnome.webm",
|
||||
"mimetype": "video/webm",
|
||||
"size": 123_774,
|
||||
},
|
||||
"org.matrix.msc1767.video": {
|
||||
"width": 1300,
|
||||
"height": 837,
|
||||
}
|
||||
},
|
||||
"event_id": "$event:notareal.hs",
|
||||
"origin_server_ts": 134_829_848,
|
||||
"room_id": "!roomid:notareal.hs",
|
||||
"sender": "@user:notareal.hs",
|
||||
"type": "m.video",
|
||||
});
|
||||
|
||||
assert_matches!(
|
||||
from_json_value::<AnyMessageLikeEvent>(json_data).unwrap(),
|
||||
AnyMessageLikeEvent::Video(MessageLikeEvent {
|
||||
content: VideoEventContent {
|
||||
message,
|
||||
file: FileContent {
|
||||
url,
|
||||
info: Some(info),
|
||||
..
|
||||
},
|
||||
video,
|
||||
thumbnail,
|
||||
caption,
|
||||
..
|
||||
},
|
||||
event_id,
|
||||
origin_server_ts,
|
||||
room_id,
|
||||
sender,
|
||||
unsigned
|
||||
}) if event_id == event_id!("$event:notareal.hs")
|
||||
&& message.find_plain() == Some("Upload: my_gnome.webm")
|
||||
&& message.find_html().is_none()
|
||||
&& url == "mxc://notareal.hs/abcdef"
|
||||
&& info.name.as_deref() == Some("my_gnome.webm")
|
||||
&& info.mimetype.as_deref() == Some("video/webm")
|
||||
&& info.size == Some(uint!(123_774))
|
||||
&& video.width == Some(uint!(1300))
|
||||
&& video.height == Some(uint!(837))
|
||||
&& video.duration.is_none()
|
||||
&& thumbnail.is_empty()
|
||||
&& caption.is_empty()
|
||||
&& origin_server_ts == MilliSecondsSinceUnixEpoch(uint!(134_829_848))
|
||||
&& room_id == room_id!("!roomid:notareal.hs")
|
||||
&& sender == user_id!("@user:notareal.hs")
|
||||
&& unsigned.is_empty()
|
||||
);
|
||||
}
|
@ -119,6 +119,7 @@ unstable-msc2676 = ["ruma-common/unstable-msc2676"]
|
||||
unstable-msc2677 = ["ruma-common/unstable-msc2677"]
|
||||
unstable-msc3551 = ["ruma-common/unstable-msc3551"]
|
||||
unstable-msc3552 = ["ruma-common/unstable-msc3552"]
|
||||
unstable-msc3553 = ["ruma-common/unstable-msc3553"]
|
||||
unstable-msc3618 = ["ruma-federation-api/unstable-msc3618"]
|
||||
unstable-msc3723 = ["ruma-federation-api/unstable-msc3723"]
|
||||
|
||||
@ -133,6 +134,7 @@ __ci = [
|
||||
"unstable-msc2677",
|
||||
"unstable-msc3551",
|
||||
"unstable-msc3552",
|
||||
"unstable-msc3553",
|
||||
"unstable-msc3618",
|
||||
"unstable-msc3723",
|
||||
"ruma-state-res/__ci",
|
||||
|
Loading…
x
Reference in New Issue
Block a user