plugin XEP-0084: Support multiple images format:

XEP-0084 implementation was only supporting PNG, but some client (notably Conversations)
don't publish PNG, so we have to handle a few other formats.
This commit is contained in:
Goffi 2026-06-21 01:51:58 +02:00
commit b65325c1d6

View file

@ -35,6 +35,9 @@ from tx_xmpp import disco, itx_xmpp, pubsub
log = getLogger(__name__)
# Supported image formats, in priority order
FORMATS = ("png", "jpeg", "webp", "gif", "svg+xml")
IMPORT_NAME = "XEP-0084"
PLUGIN_INFO = {
@ -79,6 +82,9 @@ class XEP_0084:
client = self.host.get_client(profile)
defer.ensureDeferred(self.on_metadata_update(client, itemsEvent))
class UnsupportedFormat(Exception):
"""Avatar metadata exists but no supported format is found."""
@staticmethod
def _parse_metadata_info(
item_elt: domish.Element,
@ -86,8 +92,8 @@ class XEP_0084:
"""Parse avatar metadata item and extract ID, media type, and size.
@param item_elt: pubsub item element containing metadata.
@return: (avatar_id, media_type, size) if a valid info element is found,
None if avatar is disabled or metadata is invalid.
@return: (avatar_id, media_type, size) if a supported format is found,
None if avatar is disabled or no supported format is found.
"""
try:
metadata_elt = next(item_elt.elements(NS_AVATAR_METADATA, "metadata"))
@ -95,6 +101,8 @@ class XEP_0084:
log.warning(f"missing metadata element: {item_elt.toXml()}")
return None
# (priority, id, type, size)
candidates: list[tuple[int, str, str, int]] = []
for info_elt in metadata_elt.elements(NS_AVATAR_METADATA, "info"):
try:
avatar_id = str(info_elt["id"])
@ -104,14 +112,26 @@ class XEP_0084:
log.warning(f"invalid <info> element: {item_elt.toXml()}")
continue
if not avatar_id:
# Avatar is explicitly disabled
# (https://xmpp.org/extensions/xep-0084.html#pub-disable)
return None
if not media_type.startswith("image/"):
continue
# FIXME: to simplify, we only handle image/png for now
if media_type == "image/png":
return (avatar_id, media_type, size)
subtype = media_type.split("/", 1)[1].lower()
try:
priority = FORMATS.index(subtype)
except ValueError:
continue
candidates.append((priority, avatar_id, media_type, size))
# mandatory image/png is missing, or avatar is disabled
# (https://xmpp.org/extensions/xep-0084.html#pub-disable)
return None
if not candidates:
raise XEP_0084.UnsupportedFormat(
f"no supported format found in avatar metadata"
)
candidates.sort(key=lambda c: c[0])
_, avatar_id, media_type, size = candidates[0]
return (avatar_id, media_type, size)
async def on_metadata_update(
self, client: SatXMPPEntity, itemsEvent: pubsub.ItemsEvent
@ -130,7 +150,11 @@ class XEP_0084:
await self._i.update(client, IMPORT_NAME, "avatar", None, entity)
return
parsed = self._parse_metadata_info(items[0])
try:
parsed = self._parse_metadata_info(items[0])
except XEP_0084.UnsupportedFormat:
log.warning(f"Avatar for {entity.full()} is in unsupported format.")
parsed = None
if parsed is None:
log.debug(f"Avatar disabled for {entity.full()}")
await self._i.update(client, IMPORT_NAME, "avatar", None, entity)
@ -223,7 +247,10 @@ class XEP_0084:
if not data_items:
return None
parsed = self._parse_metadata_info(data_items[0])
try:
parsed = self._parse_metadata_info(data_items[0])
except XEP_0084.UnsupportedFormat:
return None
if parsed is None:
return None