From a44cc41933e43b4de80a1e6d77ffaf86440001aa Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 1 Apr 2026 21:13:44 +0300 Subject: [PATCH] tomatrix: bridge live photos as videos --- pkg/connector/directdownload.go | 15 ++++++--- pkg/connector/media/transfer.go | 6 ++++ pkg/connector/tomatrix.go | 54 +++++++++++++++++++++++++++------ 3 files changed, 61 insertions(+), 14 deletions(-) diff --git a/pkg/connector/directdownload.go b/pkg/connector/directdownload.go index 36f2047b..878ab6e0 100644 --- a/pkg/connector/directdownload.go +++ b/pkg/connector/directdownload.go @@ -141,10 +141,17 @@ func (tc *TelegramConnector) Download(ctx context.Context, mediaID networkid.Med switch msgMedia := rawMsgMedia.(type) { case *tg.MessageMediaPhoto: - log.Debug(). - Int64("photo_id", msgMedia.Photo.GetID()). - Msg("downloading photo") - readyTransferer = transferer.WithPhoto(msgMedia.Photo) + if msgMedia.Video != nil && !info.Thumbnail { + log.Debug(). + Int64("document_id", msgMedia.Video.GetID()). + Msg("downloading live photo") + readyTransferer = transferer.WithDocument(msgMedia.Video, false) + } else { + log.Debug(). + Int64("photo_id", msgMedia.Photo.GetID()). + Msg("downloading photo") + readyTransferer = transferer.WithPhoto(msgMedia.Photo) + } case *tg.MessageMediaDocument: document, ok := msgMedia.Document.(*tg.Document) if !ok { diff --git a/pkg/connector/media/transfer.go b/pkg/connector/media/transfer.go index 4f1d66a0..94027f56 100644 --- a/pkg/connector/media/transfer.go +++ b/pkg/connector/media/transfer.go @@ -225,6 +225,12 @@ func (t *Transferer) WithDocument(doc tg.DocumentClass, thumbnail bool) *ReadyTr return &ReadyTransferer{t, &documentFileLocation} } +func (t *Transferer) WithLivePhoto(pc tg.PhotoClass, doc tg.DocumentClass) *ReadyTransferer { + photo := pc.(*tg.Photo) + t.fileInfo.Width, t.fileInfo.Height, _, _ = getLargestPhotoSize(photo.GetSizes()) + return t.WithDocument(doc, false) +} + // WithPhoto transforms a [Transferer] to a [ReadyTransferer] by setting the // given photo as the location that will be downloaded by the // [ReadyTransferer]. diff --git a/pkg/connector/tomatrix.go b/pkg/connector/tomatrix.go index a12a73d2..3720bba6 100644 --- a/pkg/connector/tomatrix.go +++ b/pkg/connector/tomatrix.go @@ -61,7 +61,9 @@ func mediaHashID(ctx context.Context, m tg.MessageMediaClass) []byte { } switch media := m.(type) { case *tg.MessageMediaPhoto: - if media.Photo != nil { + if media.Video != nil { + return binary.BigEndian.AppendUint64(nil, uint64(media.Video.GetID())) + } else if media.Photo != nil { return binary.BigEndian.AppendUint64(nil, uint64(media.Photo.GetID())) } else { zerolog.Ctx(ctx).Debug().Msg("Attempted to get hash for nil photo") @@ -427,6 +429,8 @@ func (c *TelegramClient) convertMediaRequiringUpload( var telegramMediaID int64 var isSticker, isVideo, isVideoGif bool extra := map[string]any{} + // FIXME don't use raw map for fields in the FileInfo struct + extraInfo := map[string]any{} transferer := media.NewTransferer(c.client.API()).WithRoomID(portal.MXID) var mediaTransferer *media.ReadyTransferer @@ -482,8 +486,40 @@ func (c *TelegramClient) convertMediaRequiringUpload( } return } - telegramMediaID = photo.GetID() - mediaTransferer = transferer.WithPhoto(photo) + if video, ok := msgMedia.Video.(*tg.Document); ok { + content.MsgType = event.MsgVideo + telegramMediaID = video.GetID() + mediaTransferer = transferer.WithLivePhoto(photo, video) + extraInfo["fi.mau.telegram.live_photo"] = true + + // TODO deduplicate with document thumbnail code + var thumbnailURL id.ContentURIString + var thumbnailFile *event.EncryptedFileInfo + var thumbnailInfo *event.FileInfo + var err error + + thumbnailTransferer := media.NewTransferer(c.client.API()). + WithRoomID(portal.MXID). + WithPhoto(photo) + if c.main.useDirectMedia { + thumbnailURL, thumbnailInfo, err = thumbnailTransferer.DirectDownloadURL(ctx, c.telegramUserID, portal, msgID, false, photo.ID) + if err != nil { + log.Err(err).Msg("Failed to create direct download URL for thumbnail") + } + } + if thumbnailURL == "" { + thumbnailURL, thumbnailFile, thumbnailInfo, err = thumbnailTransferer.Transfer(ctx, c.main.Store, intent) + if err != nil { + log.Err(err).Msg("Failed to transfer thumbnail") + } + } + if thumbnailURL != "" || thumbnailFile != nil { + transferer = transferer.WithThumbnail(thumbnailURL, thumbnailFile, thumbnailInfo) + } + } else { + telegramMediaID = photo.GetID() + mediaTransferer = transferer.WithPhoto(photo) + } case *tg.MessageMediaDocument: document, ok := msgMedia.Document.(*tg.Document) if !ok { @@ -500,7 +536,6 @@ func (c *TelegramClient) convertMediaRequiringUpload( content.MsgType = event.MsgFile - extraInfo := map[string]any{} for _, attr := range document.GetAttributes() { switch a := attr.(type) { case *tg.DocumentAttributeFilename: @@ -593,7 +628,6 @@ func (c *TelegramClient) convertMediaRequiringUpload( extraInfo["fi.mau.hide_controls"] = true extraInfo["fi.mau.no_audio"] = true } - extra["info"] = extraInfo if _, ok := document.GetThumbs(); ok && eventType != event.EventSticker { var thumbnailURL id.ContentURIString @@ -678,11 +712,11 @@ func (c *TelegramClient) convertMediaRequiringUpload( extra["town.robin.msc3725.content_warning"] = map[string]any{ "type": "town.robin.msc3725.spoiler", } - if extra["info"] == nil { - extra["info"] = map[string]any{} - } - info := extra["info"].(map[string]any) - info["fi.mau.telegram.spoiler"] = true + extra["page.codeberg.everypizza.msc4193.spoiler"] = true + extraInfo["fi.mau.telegram.spoiler"] = true + } + if len(extraInfo) > 0 { + extra["info"] = extraInfo } converted = &bridgev2.ConvertedMessagePart{