Files
mautrix-telegram/pkg/connector/media/transfer.go
T
Sumner Evans a63f264804 reactions: support custom emojis
Signed-off-by: Sumner Evans <sumner.evans@automattic.com>
2024-07-09 14:04:25 -06:00

128 lines
3.8 KiB
Go

package media
import (
"context"
"fmt"
"github.com/gotd/td/telegram/downloader"
"github.com/gotd/td/tg"
"maunium.net/go/mautrix/bridgev2"
"maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/id"
"go.mau.fi/util/lottie"
"go.mau.fi/mautrix-telegram/pkg/connector/store"
)
// getLocationID converts a Telegram [tg.Document],
// [tg.InputDocumentFileLocation], [tg.InputPeerPhotoFileLocation],
// [tg.InputFileLocation], or [tg.InputPhotoFileLocation] into a [LocationID]
// for use in the telegram_file table.
func getLocationID(loc any) (locID store.TelegramFileLocationID) {
var id string
switch location := loc.(type) {
case *tg.Document:
id = fmt.Sprintf("%d", location.ID)
case *tg.InputDocumentFileLocation:
id = fmt.Sprintf("%d-%s", location.ID, location.ThumbSize)
case *tg.InputPhotoFileLocation:
id = fmt.Sprintf("%d-%s", location.ID, location.ThumbSize)
case *tg.InputFileLocation:
id = fmt.Sprintf("%d-%d", location.VolumeID, location.LocalID)
case *tg.InputPeerPhotoFileLocation:
id = fmt.Sprintf("%d", location.PhotoID)
default:
panic(fmt.Errorf("unknown location type %T", location))
}
return store.TelegramFileLocationID(id)
}
type AnimatedStickerConfig struct {
Target string `yaml:"target"`
ConvertFromWebm bool `yaml:"convert_from_webm"`
Args struct {
Width int `yaml:"width"`
Height int `yaml:"height"`
FPS int `yaml:"fps"`
} `yaml:"args"`
}
func (c AnimatedStickerConfig) TGSConvert() bool {
return c.Target == "gif" || c.Target == "png"
}
func (c AnimatedStickerConfig) WebmConvert() bool {
return c.ConvertFromWebm && c.Target != "webm"
}
type Transferer struct {
RoomID id.RoomID
Filename string
IsSticker bool
Config AnimatedStickerConfig
}
func NewTransferer(cfg AnimatedStickerConfig) *Transferer {
return &Transferer{Config: cfg}
}
func (t *Transferer) WithRoomID(roomID id.RoomID) *Transferer {
t.RoomID = roomID
return t
}
func (t *Transferer) WithFilename(filename string) *Transferer {
t.Filename = filename
return t
}
func (t *Transferer) WithIsSticker(isSticker bool) *Transferer {
t.IsSticker = isSticker
return t
}
func (t *Transferer) Transfer(ctx context.Context, store *store.Container, client downloader.Client, intent bridgev2.MatrixAPI, loc tg.InputFileLocationClass) (mxc id.ContentURIString, encryptedFileInfo *event.EncryptedFileInfo, size int, mimeType string, err error) {
locationID := getLocationID(loc)
if file, err := store.TelegramFile.GetByLocationID(ctx, locationID); err != nil {
return "", nil, 0, "", fmt.Errorf("failed to search for Telegram file by location ID: %w", err)
} else if file != nil {
return file.MXC, nil, file.Size, file.MIMEType, nil
}
var data []byte
data, mimeType, err = DownloadFileLocation(ctx, client, loc)
if err != nil {
return "", nil, 0, "", fmt.Errorf("downloading file failed: %w", err)
}
if t.IsSticker {
if lottie.Supported() && t.Config.TGSConvert() && mimeType == "application/x-gzip" {
data, err = lottie.ConvertBytes(ctx, data, t.Config.Target, t.Config.Args.Width, t.Config.Args.Height, fmt.Sprintf("%d", t.Config.Args.FPS))
if err != nil {
return "", nil, 0, "", err
}
mimeType = fmt.Sprintf("image/%s", t.Config.Target)
// TODO support ffmpeg conversion
// } else if ffmpeg.Supported() && t.Config.WebmConvert() && mimeType == "video/webm" {
}
}
mxcURI, encryptedFileInfo, err := intent.UploadMedia(ctx, t.RoomID, data, t.Filename, mimeType)
if err != nil {
return "", nil, 0, "", err
}
if len(mxcURI) > 0 {
file := store.TelegramFile.New()
file.LocationID = locationID
file.MXC = mxcURI
file.Size = len(data)
file.MIMEType = mimeType
// TODO width, height, thumbnail?
if err = file.Insert(ctx); err != nil {
return "", nil, 0, "", fmt.Errorf("failed to insert Telegram file into database: %w", err)
}
}
return mxcURI, encryptedFileInfo, len(data), mimeType, nil
}