pkg/download -> pkg/media

Signed-off-by: Sumner Evans <sumner.evans@automattic.com>
This commit is contained in:
Sumner Evans
2024-07-02 12:04:09 -06:00
parent 0921168b91
commit 5d39fc8c5f
7 changed files with 133 additions and 103 deletions
+3 -3
View File
@@ -17,8 +17,8 @@ import (
"maunium.net/go/mautrix/bridgev2"
"maunium.net/go/mautrix/bridgev2/networkid"
"go.mau.fi/mautrix-telegram/pkg/connector/download"
"go.mau.fi/mautrix-telegram/pkg/connector/ids"
"go.mau.fi/mautrix-telegram/pkg/connector/media"
"go.mau.fi/mautrix-telegram/pkg/connector/msgconv"
"go.mau.fi/mautrix-telegram/pkg/connector/util"
)
@@ -241,7 +241,7 @@ func (t *TelegramClient) GetChatInfo(ctx context.Context, portal *bridgev2.Porta
avatar = &bridgev2.Avatar{
ID: ids.MakeAvatarID(photo.ID),
Get: func(ctx context.Context) (data []byte, err error) {
data, _, _, _, err = download.DownloadPhoto(ctx, t.client.API(), photo)
data, _, _, _, err = media.DownloadPhoto(ctx, t.client.API(), photo)
return
},
}
@@ -304,7 +304,7 @@ func (t *TelegramClient) getUserInfoFromTelegramUser(user *tg.User) (*bridgev2.U
avatar = &bridgev2.Avatar{
ID: ids.MakeAvatarID(photo.PhotoID),
Get: func(ctx context.Context) (data []byte, err error) {
data, _, err = download.DownloadPhotoFileLocation(ctx, t.client.API(), &tg.InputPeerPhotoFileLocation{
data, _, err = media.DownloadFileLocation(ctx, t.client.API(), &tg.InputPeerPhotoFileLocation{
Peer: &tg.InputPeerUser{UserID: user.ID},
PhotoID: photo.PhotoID,
Big: true,
+12 -11
View File
@@ -12,8 +12,8 @@ import (
"maunium.net/go/mautrix/bridgev2/networkid"
"maunium.net/go/mautrix/mediaproxy"
"go.mau.fi/mautrix-telegram/pkg/connector/download"
"go.mau.fi/mautrix-telegram/pkg/connector/ids"
"go.mau.fi/mautrix-telegram/pkg/connector/media"
)
var _ bridgev2.DirectMediableNetwork = (*TelegramConnector)(nil)
@@ -63,14 +63,14 @@ func (tc *TelegramConnector) Download(ctx context.Context, mediaID networkid.Med
return nil, err
}
var media tg.MessageMediaClass
var msgMedia tg.MessageMediaClass
if m, ok := messages.(getMessages); !ok {
return nil, fmt.Errorf("unknown message type %T", messages)
} else {
var found bool
for _, message := range m.GetMessages() {
if msg, ok := message.(*tg.Message); ok && msg.ID == int(info.MessageID) {
media = msg.Media
msgMedia = msg.Media
found = true
break
}
@@ -82,18 +82,19 @@ func (tc *TelegramConnector) Download(ctx context.Context, mediaID networkid.Med
var data []byte
var mimeType string
switch media := media.(type) {
switch msgMedia := msgMedia.(type) {
case *tg.MessageMediaPhoto:
data, _, _, mimeType, err = download.DownloadPhotoMedia(ctx, client.client.API(), media)
data, _, _, mimeType, err = media.DownloadPhotoMedia(ctx, client.client.API(), msgMedia)
case *tg.MessageMediaDocument:
document, ok := media.Document.(*tg.Document)
document, ok := msgMedia.Document.(*tg.Document)
if !ok {
return nil, fmt.Errorf("unrecognized document type %T", media.Document)
return nil, fmt.Errorf("unrecognized document type %T", msgMedia.Document)
}
// Download the thumbnail for this media rather than the media itself.
if info.Thumbnail {
_, _, largestThumbnail := download.GetLargestPhotoSize(document.Thumbs)
data, mimeType, err = download.DownloadPhotoFileLocation(ctx, client.client.API(), &tg.InputDocumentFileLocation{
_, _, largestThumbnail := media.GetLargestPhotoSize(document.Thumbs)
data, mimeType, err = media.DownloadFileLocation(ctx, client.client.API(), &tg.InputDocumentFileLocation{
ID: document.GetID(),
AccessHash: document.GetAccessHash(),
FileReference: document.GetFileReference(),
@@ -101,10 +102,10 @@ func (tc *TelegramConnector) Download(ctx context.Context, mediaID networkid.Med
})
} else {
mimeType = document.GetMimeType()
data, err = download.DownloadDocument(ctx, client.client.API(), document)
data, err = media.DownloadDocument(ctx, client.client.API(), document)
}
default:
return nil, fmt.Errorf("unhandled media type %T", media)
return nil, fmt.Errorf("unhandled media type %T", msgMedia)
}
if err != nil {
return nil, err
@@ -1,7 +1,6 @@
package download
package media
import (
"bytes"
"context"
"github.com/gotd/td/telegram/downloader"
@@ -9,12 +8,10 @@ import (
)
func DownloadDocument(ctx context.Context, client downloader.Client, document *tg.Document) ([]byte, error) {
file := tg.InputDocumentFileLocation{
data, _, err := DownloadFileLocation(ctx, client, &tg.InputDocumentFileLocation{
ID: document.GetID(),
AccessHash: document.GetAccessHash(),
FileReference: document.GetFileReference(),
}
var buf bytes.Buffer
_, err := downloader.NewDownloader().Download(client, &file).Stream(ctx, &buf)
return buf.Bytes(), err
})
return data, err
}
+40
View File
@@ -0,0 +1,40 @@
package media
import (
"bytes"
"context"
"net/http"
"github.com/gotd/td/telegram/downloader"
"github.com/gotd/td/tg"
)
func DownloadFileLocation(ctx context.Context, client downloader.Client, file tg.InputFileLocationClass) (data []byte, mimeType string, err error) {
// TODO convert to streaming? stream to file?
var buf bytes.Buffer
storageFileTypeClass, err := downloader.NewDownloader().Download(client, file).Stream(ctx, &buf)
if err != nil {
return nil, "", err
}
switch storageFileTypeClass.(type) {
case *tg.StorageFileJpeg:
mimeType = "image/jpeg"
case *tg.StorageFileGif:
mimeType = "image/gif"
case *tg.StorageFilePng:
mimeType = "image/png"
case *tg.StorageFilePdf:
mimeType = "application/pdf"
case *tg.StorageFileMp3:
mimeType = "audio/mp3"
case *tg.StorageFileMov:
mimeType = "video/quicktime"
case *tg.StorageFileMp4:
mimeType = "video/mp4"
case *tg.StorageFileWebp:
mimeType = "image/webp"
default:
mimeType = http.DetectContentType(buf.Bytes())
}
return buf.Bytes(), mimeType, nil
}
@@ -1,10 +1,8 @@
package download
package media
import (
"bytes"
"context"
"fmt"
"net/http"
"github.com/gotd/td/telegram/downloader"
"github.com/gotd/td/tg"
@@ -48,54 +46,10 @@ func GetLargestPhotoSize(sizes []tg.PhotoSizeClass) (width, height int, largest
return
}
func GetLargestDimensions(sizes []tg.PhotoSizeClass) (width, height int) {
for _, s := range sizes {
switch size := s.(type) {
case *tg.PhotoCachedSize:
width = size.GetW()
height = size.GetH()
case *tg.PhotoSizeProgressive:
width = size.GetW()
height = size.GetH()
}
}
return
}
func DownloadPhotoFileLocation(ctx context.Context, client downloader.Client, file tg.InputFileLocationClass) (data []byte, mimeType string, err error) {
// TODO convert to streaming?
var buf bytes.Buffer
storageFileTypeClass, err := downloader.NewDownloader().Download(client, file).Stream(ctx, &buf)
if err != nil {
return nil, "", err
}
switch storageFileTypeClass.(type) {
case *tg.StorageFileJpeg:
mimeType = "image/jpeg"
case *tg.StorageFileGif:
mimeType = "image/gif"
case *tg.StorageFilePng:
mimeType = "image/png"
case *tg.StorageFilePdf:
mimeType = "application/pdf"
case *tg.StorageFileMp3:
mimeType = "audio/mp3"
case *tg.StorageFileMov:
mimeType = "video/quicktime"
case *tg.StorageFileMp4:
mimeType = "video/mp4"
case *tg.StorageFileWebp:
mimeType = "image/webp"
default:
mimeType = http.DetectContentType(buf.Bytes())
}
return buf.Bytes(), mimeType, nil
}
func DownloadPhoto(ctx context.Context, client downloader.Client, photo *tg.Photo) (data []byte, width, height int, mimeType string, err error) {
var largest tg.PhotoSizeClass
width, height, largest = GetLargestPhotoSize(photo.GetSizes())
data, mimeType, err = DownloadPhotoFileLocation(ctx, client, &tg.InputPhotoFileLocation{
data, mimeType, err = DownloadFileLocation(ctx, client, &tg.InputPhotoFileLocation{
ID: photo.GetID(),
AccessHash: photo.GetAccessHash(),
FileReference: photo.GetFileReference(),
@@ -105,13 +59,11 @@ func DownloadPhoto(ctx context.Context, client downloader.Client, photo *tg.Phot
}
func DownloadPhotoMedia(ctx context.Context, client downloader.Client, media *tg.MessageMediaPhoto) (data []byte, width, height int, mimeType string, err error) {
p, ok := media.GetPhoto()
if !ok {
if p, ok := media.GetPhoto(); !ok {
return nil, 0, 0, "", fmt.Errorf("photo message sent without a photo")
}
photo, ok := p.(*tg.Photo)
if !ok {
} else if photo, ok := p.(*tg.Photo); !ok {
return nil, 0, 0, "", fmt.Errorf("unrecognized photo type %T", p)
} else {
return DownloadPhoto(ctx, client, photo)
}
return DownloadPhoto(ctx, client, photo)
}
+45
View File
@@ -0,0 +1,45 @@
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"
)
// LocationToID converts a Telegram [tg.Document],
// [tg.InputDocumentFileLocation], [tg.InputPeerPhotoFileLocation],
// [tg.InputFileLocation], or [tg.InputPhotoFileLocation] into a key for use in
// the telegram_file table.
func LocationToID(location any) (id string) {
switch location := location.(type) {
case *tg.Document:
return fmt.Sprintf("%d", location.ID)
case *tg.InputDocumentFileLocation:
return fmt.Sprintf("%d-%s", location.ID, location.ThumbSize)
case *tg.InputPhotoFileLocation:
return fmt.Sprintf("%d-%s", location.ID, location.ThumbSize)
case *tg.InputFileLocation:
return fmt.Sprintf("%d-%d", location.VolumeID, location.LocalID)
case *tg.InputPeerPhotoFileLocation:
return fmt.Sprintf("%d", location.PhotoID)
default:
panic(fmt.Errorf("unknown location type %T", location))
}
}
func TransferToMatrix(ctx context.Context, roomID id.RoomID, client downloader.Client, intent bridgev2.MatrixAPI, file tg.InputFileLocationClass, filenameOpt ...string) (id.ContentURIString, *event.EncryptedFileInfo, error) {
data, mimeType, err := DownloadFileLocation(ctx, client, file)
if err != nil {
return "", nil, fmt.Errorf("downloading file failed: %w", err)
}
var filename string
if len(filenameOpt) > 0 {
filename = filenameOpt[0]
}
return intent.UploadMedia(ctx, roomID, data, filename, mimeType)
}
+23 -28
View File
@@ -16,8 +16,8 @@ import (
"maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/id"
"go.mau.fi/mautrix-telegram/pkg/connector/download"
"go.mau.fi/mautrix-telegram/pkg/connector/ids"
"go.mau.fi/mautrix-telegram/pkg/connector/media"
"go.mau.fi/mautrix-telegram/pkg/connector/util"
"go.mau.fi/mautrix-telegram/pkg/connector/waveform"
)
@@ -119,8 +119,8 @@ func (mc *MessageConverter) ToMatrix(ctx context.Context, portal *bridgev2.Porta
return cm, nil
}
func (mc *MessageConverter) webpageToBeeperLinkPreview(ctx context.Context, intent bridgev2.MatrixAPI, media tg.MessageMediaClass) (preview *event.BeeperLinkPreview, err error) {
webpage, ok := media.(*tg.MessageMediaWebPage).Webpage.(*tg.WebPage)
func (mc *MessageConverter) webpageToBeeperLinkPreview(ctx context.Context, intent bridgev2.MatrixAPI, msgMedia tg.MessageMediaClass) (preview *event.BeeperLinkPreview, err error) {
webpage, ok := msgMedia.(*tg.MessageMediaWebPage).Webpage.(*tg.WebPage)
if !ok {
return nil, nil
}
@@ -135,7 +135,7 @@ func (mc *MessageConverter) webpageToBeeperLinkPreview(ctx context.Context, inte
if pc, ok := webpage.GetPhoto(); ok && pc.TypeID() == tg.PhotoTypeID {
var data []byte
data, preview.ImageWidth, preview.ImageHeight, preview.ImageType, err = download.DownloadPhoto(ctx, mc.client.API(), pc.(*tg.Photo))
data, preview.ImageWidth, preview.ImageHeight, preview.ImageType, err = media.DownloadPhoto(ctx, mc.client.API(), pc.(*tg.Photo))
if err != nil {
return nil, err
}
@@ -170,7 +170,7 @@ func (mc *MessageConverter) directMedia(ctx context.Context, portal *bridgev2.Po
return portal.Bridge.Matrix.GenerateContentURI(ctx, mediaID)
}
func (mc *MessageConverter) convertMediaRequiringUpload(ctx context.Context, portal *bridgev2.Portal, intent bridgev2.MatrixAPI, msgID int, media tg.MessageMediaClass) (*bridgev2.ConvertedMessagePart, *database.DisappearingSetting, error) {
func (mc *MessageConverter) convertMediaRequiringUpload(ctx context.Context, portal *bridgev2.Portal, intent bridgev2.MatrixAPI, msgID int, msgMedia tg.MessageMediaClass) (*bridgev2.ConvertedMessagePart, *database.DisappearingSetting, error) {
var partID networkid.PartID
var msgType event.MessageType
var filename string
@@ -179,27 +179,27 @@ func (mc *MessageConverter) convertMediaRequiringUpload(ctx context.Context, por
var info event.FileInfo
// Determine the filename and some other information
switch media := media.(type) {
switch msgMedia := msgMedia.(type) {
case *tg.MessageMediaPhoto:
partID = networkid.PartID("photo")
msgType = event.MsgImage
filename = "image"
if photo, ok := media.Photo.(*tg.Photo); ok {
info.Width, info.Height, _ = download.GetLargestPhotoSize(photo.GetSizes())
if photo, ok := msgMedia.Photo.(*tg.Photo); ok {
info.Width, info.Height, _ = media.GetLargestPhotoSize(photo.GetSizes())
}
case *tg.MessageMediaDocument:
partID = networkid.PartID("document")
msgType = event.MsgFile
document, ok := media.Document.(*tg.Document)
document, ok := msgMedia.Document.(*tg.Document)
info.Size = int(document.Size)
if !ok {
return nil, nil, fmt.Errorf("unrecognized document type %T", media.Document)
return nil, nil, fmt.Errorf("unrecognized document type %T", msgMedia.Document)
}
if thumbSizes, ok := document.GetThumbs(); ok {
info.ThumbnailInfo = &event.FileInfo{}
var largestThumbnail tg.PhotoSizeClass
info.ThumbnailInfo.Width, info.ThumbnailInfo.Height, largestThumbnail = download.GetLargestPhotoSize(thumbSizes)
info.ThumbnailInfo.Width, info.ThumbnailInfo.Height, largestThumbnail = media.GetLargestPhotoSize(thumbSizes)
var err error
info.ThumbnailInfo.ThumbnailURL, err = mc.directMedia(ctx, portal, msgID, true)
@@ -208,17 +208,12 @@ func (mc *MessageConverter) convertMediaRequiringUpload(ctx context.Context, por
}
if info.ThumbnailInfo.ThumbnailURL == "" {
data, mimeType, err := download.DownloadPhotoFileLocation(ctx, mc.client.API(), &tg.InputDocumentFileLocation{
info.ThumbnailInfo.ThumbnailURL, info.ThumbnailInfo.ThumbnailFile, err = media.TransferToMatrix(ctx, portal.MXID, mc.client.API(), intent, &tg.InputDocumentFileLocation{
ID: document.GetID(),
AccessHash: document.GetAccessHash(),
FileReference: document.GetFileReference(),
ThumbSize: largestThumbnail.GetType(),
})
if err != nil {
return nil, nil, fmt.Errorf("downloading thumbnail failed: %w", err)
}
info.ThumbnailInfo.ThumbnailURL, info.ThumbnailInfo.ThumbnailFile, err = intent.UploadMedia(ctx, "", data, filename, mimeType)
if err != nil {
return nil, nil, err
}
@@ -249,7 +244,7 @@ func (mc *MessageConverter) convertMediaRequiringUpload(ctx context.Context, por
}
}
default:
return nil, nil, fmt.Errorf("unhandled media type %T", media)
return nil, nil, fmt.Errorf("unhandled media type %T", msgMedia)
}
var encryptedFileInfo *event.EncryptedFileInfo
@@ -263,31 +258,31 @@ func (mc *MessageConverter) convertMediaRequiringUpload(ctx context.Context, por
var data []byte
var mimeType string
var err error
switch media := media.(type) {
switch msgMedia := msgMedia.(type) {
case *tg.MessageMediaPhoto:
if _, ok := media.GetTTLSeconds(); ok {
if _, ok := msgMedia.GetTTLSeconds(); ok {
filename = "disappearing_image" + exmime.ExtensionFromMimetype(mimeType)
} else {
filename = "image" + exmime.ExtensionFromMimetype(mimeType)
}
data, _, _, mimeType, err = download.DownloadPhotoMedia(ctx, mc.client.API(), media)
data, _, _, mimeType, err = media.DownloadPhotoMedia(ctx, mc.client.API(), msgMedia)
case *tg.MessageMediaDocument:
document, ok := media.Document.(*tg.Document)
document, ok := msgMedia.Document.(*tg.Document)
if !ok {
return nil, nil, fmt.Errorf("unrecognized document type %T", media.Document)
return nil, nil, fmt.Errorf("unrecognized document type %T", msgMedia.Document)
}
mimeType = document.GetMimeType()
data, err = download.DownloadDocument(ctx, mc.client.API(), document)
data, err = media.DownloadDocument(ctx, mc.client.API(), document)
default:
return nil, nil, fmt.Errorf("unhandled media type %T", media)
return nil, nil, fmt.Errorf("unhandled media type %T", msgMedia)
}
if err != nil {
return nil, nil, err
}
mxcURI, encryptedFileInfo, err = intent.UploadMedia(ctx, "", data, filename, mimeType)
mxcURI, encryptedFileInfo, err = intent.UploadMedia(ctx, portal.MXID, data, filename, mimeType)
if err != nil {
return nil, nil, err
}
@@ -297,7 +292,7 @@ func (mc *MessageConverter) convertMediaRequiringUpload(ctx context.Context, por
// Handle spoilers
// See: https://github.com/matrix-org/matrix-spec-proposals/pull/3725
if s, ok := media.(spoilable); ok && s.GetSpoiler() {
if s, ok := msgMedia.(spoilable); ok && s.GetSpoiler() {
extra["town.robin.msc3725.content_warning"] = map[string]any{
"type": "town.robin.msc3725.spoiler",
}
@@ -306,7 +301,7 @@ func (mc *MessageConverter) convertMediaRequiringUpload(ctx context.Context, por
// Handle disappearing messages
var disappearingSetting *database.DisappearingSetting
if t, ok := media.(ttlable); ok {
if t, ok := msgMedia.(ttlable); ok {
if ttl, ok := t.GetTTLSeconds(); ok {
disappearingSetting = &database.DisappearingSetting{
Type: database.DisappearingTypeAfterSend,