From 04244ecf826d206c85534ffc7b1ed0df8f8101a8 Mon Sep 17 00:00:00 2001 From: mathieui Date: Sun, 2 Feb 2025 12:16:07 +0100 Subject: [PATCH] XEP-0446: complete support and tests --- doap.xml | 8 ++ slixmpp/plugins/__init__.py | 1 + slixmpp/plugins/xep_0446/file_metadata.py | 4 + slixmpp/plugins/xep_0446/stanza.py | 53 ++++++++++-- tests/test_stanza_xep_0446.py | 101 ++++++++++++++++++++++ 5 files changed, 160 insertions(+), 7 deletions(-) create mode 100644 tests/test_stanza_xep_0446.py diff --git a/doap.xml b/doap.xml index 9d3c66c7..20ad179d 100644 --- a/doap.xml +++ b/doap.xml @@ -916,6 +916,14 @@ 1.6.0 + + + + complete + 0.2.0 + 1.8.7 + + diff --git a/slixmpp/plugins/__init__.py b/slixmpp/plugins/__init__.py index dd43077c..589cef46 100644 --- a/slixmpp/plugins/__init__.py +++ b/slixmpp/plugins/__init__.py @@ -118,6 +118,7 @@ PLUGINS = [ 'xep_0439', # Quick Response 'xep_0441', # Message Archive Management Preferences 'xep_0444', # Message Reactions + 'xep_0446', # File metadata element 'xep_0447', # Stateless file sharing 'xep_0461', # Message Replies 'xep_0469', # Bookmarks Pinning diff --git a/slixmpp/plugins/xep_0446/file_metadata.py b/slixmpp/plugins/xep_0446/file_metadata.py index e917f6b4..56c7e01b 100644 --- a/slixmpp/plugins/xep_0446/file_metadata.py +++ b/slixmpp/plugins/xep_0446/file_metadata.py @@ -18,3 +18,7 @@ class XEP_0446(BasePlugin): name = "xep_0446" description = "XEP-0446: File metadata element" stanza = stanza + dependencies = {'xep_0300', 'xep_0264'} + + def plugin_init(self): + stanza.register_plugins() diff --git a/slixmpp/plugins/xep_0446/stanza.py b/slixmpp/plugins/xep_0446/stanza.py index f887272d..4335383b 100644 --- a/slixmpp/plugins/xep_0446/stanza.py +++ b/slixmpp/plugins/xep_0446/stanza.py @@ -1,7 +1,10 @@ from datetime import datetime +from typing import Optional from slixmpp.plugins.xep_0082 import format_datetime, parse -from slixmpp.xmlstream import ElementBase +from slixmpp.plugins.xep_0300 import Hash +from slixmpp.plugins.xep_0264.stanza import Thumbnail +from slixmpp.xmlstream import ElementBase, register_stanza_plugin NS = "urn:xmpp:file:metadata:0" @@ -10,15 +13,42 @@ class File(ElementBase): name = "file" namespace = NS plugin_attrib = "file" - interfaces = sub_interfaces = {"media-type", "name", "date", "size", "hash", "desc"} + interfaces = sub_interfaces = { + "media-type", + "name", + "date", + "size", + "desc", + "width", + "height", + "length" + } + + def set_width(self, width: int): + self.__set_if_positive("width", width) + + def get_width(self) -> Optional[int]: + return _positive_int_or_none(self._get_sub_text("width")) + + def set_height(self, height: int): + self.__set_if_positive("height", height) + + def get_height(self) -> Optional[int]: + return _positive_int_or_none(self._get_sub_text("height")) + + def set_length(self, length: int): + self.__set_if_positive("length", length) + + def get_length(self) -> Optional[int]: + return _positive_int_or_none(self._get_sub_text("length")) def set_size(self, size: int): - self._set_sub_text("size", str(size)) + self.__set_if_positive("size", size) - def get_size(self): - return _int_or_none(self._get_sub_text("size")) + def get_size(self) -> Optional[int]: + return _positive_int_or_none(self._get_sub_text("size")) - def get_date(self): + def get_date(self) -> Optional[datetime]: try: return parse(self._get_sub_text("date")) except ValueError: @@ -30,9 +60,18 @@ class File(ElementBase): except ValueError: pass + def __set_if_positive(self, key: str, value: int): + if value <= 0: + raise ValueError(f"Invalid value for element {key}: {value}") + self._set_sub_text(key, str(value)) -def _int_or_none(v): + +def _positive_int_or_none(v: str) -> Optional[int]: try: return int(v) except ValueError: return None + +def register_plugins(): + register_stanza_plugin(File, Hash) + register_stanza_plugin(File, Thumbnail) diff --git a/tests/test_stanza_xep_0446.py b/tests/test_stanza_xep_0446.py new file mode 100644 index 00000000..1a245081 --- /dev/null +++ b/tests/test_stanza_xep_0446.py @@ -0,0 +1,101 @@ +import unittest + +from slixmpp.test import SlixTest +from slixmpp.plugins.xep_0446 import stanza + + +class TestFileMeta(SlixTest): + def setUp(self): + stanza.register_plugins() + + def test_simple(self): + file = stanza.File() + file["desc"] = "a description" + file["name"] = "toto.jpg" + file["media-type"] = "image/jpeg" + file["height"] = 1024 + file["width"] = 768 + file["size"] = 2048 + self.check( + file, + """ + + a description + toto.jpg + image/jpeg + 1024 + 768 + 2048 + + """, + ) + + def test_bad_value(self): + file = stanza.File() + file["desc"] = "My great video" + file["name"] = "toto.mp4" + file["media-type"] = "video/3gpp" + file["height"] = 1024 + file["width"] = 768 + with self.assertRaises(ValueError): + file["length"] = -100 + + def test_hash_element(self): + file = stanza.File() + file["desc"] = "My great video" + file["name"] = "toto.3gp" + file["media-type"] = "video/3gpp" + file["height"] = 1024 + file["width"] = 768 + file["length"] = 2000 + file["hash"]["algo"] = "sha3-256" + file["hash"]["value"] = "abcdef=" + self.check( + file, + """ + + My great video + toto.3gp + video/3gpp + 1024 + 768 + 2000 + abcdef= + + """, + ) + + def test_thumbnail_element(self): + file = stanza.File() + file["desc"] = "a description" + file["name"] = "toto.jpg" + file["media-type"] = "image/jpeg" + file["height"] = 1024 + file["width"] = 768 + file["size"] = 2048 + file["thumbnail"]["media-type"] = "image/png" + file["thumbnail"]["uri"] = "cid:sha1+deadbeef@bob.xmpp.org" + file["thumbnail"]["width"] = 128 + file["thumbnail"]["height"] = 96 + self.check( + file, + """ + + a description + toto.jpg + image/jpeg + 1024 + 768 + 2048 + + + """, + ) + + + +suite = unittest.TestLoader().loadTestsFromTestCase(TestFileMeta)