media: refactor sticker conversion
This commit is contained in:
@@ -41,7 +41,7 @@ require (
|
|||||||
golang.org/x/sync v0.18.0
|
golang.org/x/sync v0.18.0
|
||||||
golang.org/x/tools v0.39.0
|
golang.org/x/tools v0.39.0
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
maunium.net/go/mautrix v0.26.1-0.20251202170404-7d54edbfda13
|
maunium.net/go/mautrix v0.26.1-0.20251203195941-02ce6ff91851
|
||||||
rsc.io/qr v0.2.0
|
rsc.io/qr v0.2.0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -237,7 +237,7 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
|||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
maunium.net/go/mauflag v1.0.0 h1:YiaRc0tEI3toYtJMRIfjP+jklH45uDHtT80nUamyD4M=
|
maunium.net/go/mauflag v1.0.0 h1:YiaRc0tEI3toYtJMRIfjP+jklH45uDHtT80nUamyD4M=
|
||||||
maunium.net/go/mauflag v1.0.0/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA=
|
maunium.net/go/mauflag v1.0.0/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA=
|
||||||
maunium.net/go/mautrix v0.26.1-0.20251202170404-7d54edbfda13 h1:zVuKNguVQCC51qDwNr9vnpvuurjZ2IqML2nDBmScx/k=
|
maunium.net/go/mautrix v0.26.1-0.20251203195941-02ce6ff91851 h1:5dty5IkJGxpLj0SQ2+wwKIcrPfZML1uHFcGaQIA9te0=
|
||||||
maunium.net/go/mautrix v0.26.1-0.20251202170404-7d54edbfda13/go.mod h1:NaesYcOQWFDbixVYywCVS+Twlzab9hOUpFNlCBlvciE=
|
maunium.net/go/mautrix v0.26.1-0.20251203195941-02ce6ff91851/go.mod h1:NaesYcOQWFDbixVYywCVS+Twlzab9hOUpFNlCBlvciE=
|
||||||
rsc.io/qr v0.2.0 h1:6vBLea5/NRMVTz8V66gipeLycZMl/+UlFmk8DvqQ6WY=
|
rsc.io/qr v0.2.0 h1:6vBLea5/NRMVTz8V66gipeLycZMl/+UlFmk8DvqQ6WY=
|
||||||
rsc.io/qr v0.2.0/go.mod h1:IF+uZjkb9fqyeF/4tlBoynqmQxUoPfWEKh921coOuXs=
|
rsc.io/qr v0.2.0/go.mod h1:IF+uZjkb9fqyeF/4tlBoynqmQxUoPfWEKh921coOuXs=
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ package connector
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
|
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
"maunium.net/go/mautrix"
|
"maunium.net/go/mautrix"
|
||||||
@@ -226,26 +225,7 @@ func (tc *TelegramConnector) Download(ctx context.Context, mediaID networkid.Med
|
|||||||
WithDocument(customEmojiDocuments[0], false)
|
WithDocument(customEmojiDocuments[0], false)
|
||||||
}
|
}
|
||||||
|
|
||||||
if readyTransferer == nil {
|
return readyTransferer.ToDirectMediaResponse(ctx)
|
||||||
return nil, fmt.Errorf("invalid combination of direct media keys")
|
|
||||||
}
|
|
||||||
|
|
||||||
r, mimeType, size, err := readyTransferer.Stream(ctx)
|
|
||||||
if err != nil {
|
|
||||||
log.Err(err).Msg("failed to download media")
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Debug().
|
|
||||||
Str("mime_type", mimeType).
|
|
||||||
Int("size", size).
|
|
||||||
Msg("Downloaded media successfully")
|
|
||||||
|
|
||||||
return &mediaproxy.GetMediaResponseData{
|
|
||||||
Reader: io.NopCloser(r),
|
|
||||||
ContentType: mimeType,
|
|
||||||
ContentLength: int64(size),
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tg *TelegramConnector) SetUseDirectMedia() {
|
func (tg *TelegramConnector) SetUseDirectMedia() {
|
||||||
|
|||||||
@@ -17,18 +17,13 @@
|
|||||||
package media
|
package media
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
"go.mau.fi/util/ffmpeg"
|
"go.mau.fi/util/ffmpeg"
|
||||||
"go.mau.fi/util/lottie"
|
"go.mau.fi/util/lottie"
|
||||||
"go.mau.fi/util/random"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type AnimatedStickerConfig struct {
|
type AnimatedStickerConfig struct {
|
||||||
@@ -42,7 +37,8 @@ type AnimatedStickerConfig struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ConvertedSticker struct {
|
type ConvertedSticker struct {
|
||||||
DataWriter io.Reader
|
Success bool
|
||||||
|
NewPath string
|
||||||
MIMEType string
|
MIMEType string
|
||||||
ThumbnailData []byte
|
ThumbnailData []byte
|
||||||
ThumbnailMIMEType string
|
ThumbnailMIMEType string
|
||||||
@@ -51,23 +47,82 @@ type ConvertedSticker struct {
|
|||||||
Size int
|
Size int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c AnimatedStickerConfig) convert(ctx context.Context, data []byte) ConvertedSticker {
|
func (c *AnimatedStickerConfig) convertWebm(ctx context.Context, src *os.File) *ConvertedSticker {
|
||||||
input := bytes.NewBuffer(data)
|
if !c.ConvertFromWebm || c.Target == "webm" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
log := zerolog.Ctx(ctx).With().Str("animated_sticker_target", c.Target).Logger()
|
||||||
|
if !ffmpeg.Supported() {
|
||||||
|
log.Warn().Msg("Not converting webm sticker as ffmpeg is not installed")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var newPath string
|
||||||
|
var err error
|
||||||
|
switch c.Target {
|
||||||
|
case "png":
|
||||||
|
newPath, err = ffmpeg.ConvertPath(
|
||||||
|
ctx, src.Name(), ".png",
|
||||||
|
[]string{"-ss", "0", "-c:v", "libvpx-vp9"},
|
||||||
|
[]string{"-frames:v", "1"},
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
case "gif":
|
||||||
|
newPath, err = ffmpeg.ConvertPath(
|
||||||
|
ctx, src.Name(), ".gif",
|
||||||
|
[]string{"-c:v", "libvpx-vp9"},
|
||||||
|
[]string{"-vf", "split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse"},
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
case "webp":
|
||||||
|
newPath, err = ffmpeg.ConvertPath(
|
||||||
|
ctx, src.Name(), ".webp",
|
||||||
|
[]string{"-c:v", "libvpx-vp9"},
|
||||||
|
[]string{"-loop", "0"},
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
default:
|
||||||
|
log.Error().Msg("Unknown target format for webm conversion")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
log.Err(err).Msg("Failed to convert webm sticker")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var outputSize int64
|
||||||
|
stat, err := os.Stat(newPath)
|
||||||
|
if err != nil {
|
||||||
|
log.Err(err).Msg("Failed to stat converted sticker")
|
||||||
|
} else {
|
||||||
|
outputSize = stat.Size()
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = src.Close()
|
||||||
|
return &ConvertedSticker{
|
||||||
|
Success: true,
|
||||||
|
NewPath: newPath,
|
||||||
|
MIMEType: "image/" + c.Target,
|
||||||
|
Width: c.Args.Width,
|
||||||
|
Height: c.Args.Height,
|
||||||
|
Size: int(outputSize),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *AnimatedStickerConfig) convert(ctx context.Context, src *os.File) *ConvertedSticker {
|
||||||
if c.Target == "disable" {
|
if c.Target == "disable" {
|
||||||
return ConvertedSticker{DataWriter: input, MIMEType: "video/lottie+json"}
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
log := zerolog.Ctx(ctx).With().Str("animated_sticker_target", c.Target).Logger()
|
log := zerolog.Ctx(ctx).With().Str("animated_sticker_target", c.Target).Logger()
|
||||||
|
|
||||||
if !lottie.Supported() {
|
if !lottie.Supported() {
|
||||||
log.Warn().Msg("lottie not supported, cannot convert animated stickers")
|
log.Warn().Msg("Not converting lottie sticker as lottieconverter is not installed")
|
||||||
return ConvertedSticker{DataWriter: input, MIMEType: "video/lottie+json"}
|
return nil
|
||||||
} else if (c.Target == "webp" || c.Target == "webm") && !ffmpeg.Supported() {
|
} else if (c.Target == "webp" || c.Target == "webm") && !ffmpeg.Supported() {
|
||||||
log.Warn().Msg("ffmpeg not supported, cannot convert animated stickers")
|
log.Warn().Msg("Not converting lottie sticker as target is webp/webm, but ffmpeg is not installed")
|
||||||
return ConvertedSticker{DataWriter: input, MIMEType: "video/lottie+json"}
|
return nil
|
||||||
}
|
}
|
||||||
|
outputFilename := src.Name() + "." + c.Target
|
||||||
|
|
||||||
dataWriter := new(bytes.Buffer)
|
|
||||||
var thumbnailData []byte
|
var thumbnailData []byte
|
||||||
var mimeType, thumbnailMIMEType string
|
var mimeType, thumbnailMIMEType string
|
||||||
|
|
||||||
@@ -75,44 +130,47 @@ func (c AnimatedStickerConfig) convert(ctx context.Context, data []byte) Convert
|
|||||||
switch c.Target {
|
switch c.Target {
|
||||||
case "png":
|
case "png":
|
||||||
mimeType = "image/png"
|
mimeType = "image/png"
|
||||||
err = lottie.Convert(ctx, input, "", dataWriter, c.Target, c.Args.Width, c.Args.Height, "1")
|
err = lottie.Convert(ctx, src, outputFilename, nil, c.Target, c.Args.Width, c.Args.Height, "1")
|
||||||
case "gif":
|
case "gif":
|
||||||
mimeType = "image/gif"
|
mimeType = "image/gif"
|
||||||
err = lottie.Convert(ctx, input, "", dataWriter, c.Target, c.Args.Width, c.Args.Height, strconv.Itoa(c.Args.FPS))
|
err = lottie.Convert(ctx, src, outputFilename, nil, c.Target, c.Args.Width, c.Args.Height, strconv.Itoa(c.Args.FPS))
|
||||||
case "webm", "webp":
|
case "webm", "webp":
|
||||||
tmpFile := filepath.Join(os.TempDir(), fmt.Sprintf("mautrix-telegram-lottieconverter-%s.%s", random.String(10), c.Target))
|
|
||||||
defer func() {
|
|
||||||
_ = os.Remove(tmpFile)
|
|
||||||
}()
|
|
||||||
thumbnailMIMEType = "image/png"
|
thumbnailMIMEType = "image/png"
|
||||||
mimeType = "image/" + c.Target
|
if c.Target == "webm" {
|
||||||
thumbnailData, err = lottie.FFmpegConvert(ctx, input, tmpFile, c.Args.Width, c.Args.Height, c.Args.FPS)
|
mimeType = "video/webm"
|
||||||
|
} else {
|
||||||
|
mimeType = "image/webp"
|
||||||
|
}
|
||||||
|
thumbnailData, err = lottie.FFmpegConvert(ctx, src, outputFilename, c.Args.Width, c.Args.Height, c.Args.FPS)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
var convertedData []byte
|
|
||||||
convertedData, err = os.ReadFile(tmpFile)
|
|
||||||
dataWriter = bytes.NewBuffer(convertedData)
|
|
||||||
default:
|
default:
|
||||||
err = fmt.Errorf("unsupported target format %s", c.Target)
|
log.Error().Msg("Unknown target format")
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Err(err).
|
_ = os.Remove(outputFilename)
|
||||||
Str("target", c.Target).
|
log.Err(err).Msg("Failed to convert animated sticker")
|
||||||
Msg("failed to convert animated sticker to target format")
|
return nil
|
||||||
|
}
|
||||||
// Fallback to original data
|
var outputSize int64
|
||||||
return ConvertedSticker{DataWriter: input, MIMEType: "video/lottie+json"}
|
stat, err := os.Stat(outputFilename)
|
||||||
|
if err != nil {
|
||||||
|
log.Err(err).Msg("Failed to stat converted sticker")
|
||||||
|
} else {
|
||||||
|
outputSize = stat.Size()
|
||||||
}
|
}
|
||||||
|
|
||||||
return ConvertedSticker{
|
_ = src.Close()
|
||||||
DataWriter: dataWriter,
|
return &ConvertedSticker{
|
||||||
|
Success: true,
|
||||||
|
NewPath: outputFilename,
|
||||||
MIMEType: mimeType,
|
MIMEType: mimeType,
|
||||||
ThumbnailData: thumbnailData,
|
ThumbnailData: thumbnailData,
|
||||||
ThumbnailMIMEType: thumbnailMIMEType,
|
ThumbnailMIMEType: thumbnailMIMEType,
|
||||||
Width: c.Args.Width,
|
Width: c.Args.Width,
|
||||||
Height: c.Args.Height,
|
Height: c.Args.Height,
|
||||||
Size: dataWriter.Len(),
|
Size: int(outputSize),
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,13 +21,13 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"os"
|
||||||
|
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
"go.mau.fi/util/gnuzip"
|
|
||||||
"maunium.net/go/mautrix/bridgev2"
|
"maunium.net/go/mautrix/bridgev2"
|
||||||
"maunium.net/go/mautrix/event"
|
"maunium.net/go/mautrix/event"
|
||||||
"maunium.net/go/mautrix/id"
|
"maunium.net/go/mautrix/id"
|
||||||
|
"maunium.net/go/mautrix/mediaproxy"
|
||||||
|
|
||||||
"go.mau.fi/mautrix-telegram/pkg/connector/ids"
|
"go.mau.fi/mautrix-telegram/pkg/connector/ids"
|
||||||
"go.mau.fi/mautrix-telegram/pkg/connector/store"
|
"go.mau.fi/mautrix-telegram/pkg/connector/store"
|
||||||
@@ -137,16 +137,6 @@ func (t *Transferer) WithFilename(filename string) *Transferer {
|
|||||||
// WithStickerConfig sets the animated sticker config for the [Transferer].
|
// WithStickerConfig sets the animated sticker config for the [Transferer].
|
||||||
func (t *Transferer) WithStickerConfig(cfg AnimatedStickerConfig) *Transferer {
|
func (t *Transferer) WithStickerConfig(cfg AnimatedStickerConfig) *Transferer {
|
||||||
t.animatedStickerConfig = &cfg
|
t.animatedStickerConfig = &cfg
|
||||||
switch cfg.Target {
|
|
||||||
case "png":
|
|
||||||
t.fileInfo.MimeType = "image/png"
|
|
||||||
case "gif":
|
|
||||||
t.fileInfo.MimeType = "image/gif"
|
|
||||||
case "webp":
|
|
||||||
t.fileInfo.MimeType = "image/webp"
|
|
||||||
case "webm":
|
|
||||||
t.fileInfo.MimeType = "video/webm"
|
|
||||||
}
|
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -278,43 +268,62 @@ func (t *ReadyTransferer) Transfer(ctx context.Context, store *store.Container,
|
|||||||
return "", nil, nil, fmt.Errorf("downloading file failed: %w", err)
|
return "", nil, nil, fmt.Errorf("downloading file failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if t.inner.animatedStickerConfig != nil && t.inner.fileInfo.MimeType == "application/x-tgsticker" {
|
needStickerConvert := t.inner.animatedStickerConfig != nil && (t.inner.fileInfo.MimeType == "application/x-tgsticker" ||
|
||||||
data, err := io.ReadAll(reader)
|
(t.inner.fileInfo.MimeType == "video/webm" && t.inner.animatedStickerConfig.ConvertFromWebm && t.inner.animatedStickerConfig.Target != "webm"))
|
||||||
if err != nil {
|
|
||||||
return "", nil, nil, fmt.Errorf("reading sticker data failed: %w", err)
|
|
||||||
}
|
|
||||||
converted := t.inner.animatedStickerConfig.convert(ctx, data)
|
|
||||||
reader = converted.DataWriter
|
|
||||||
t.inner.fileInfo.MimeType = converted.MIMEType
|
|
||||||
t.inner.fileInfo.Width = converted.Width
|
|
||||||
t.inner.fileInfo.Height = converted.Height
|
|
||||||
t.inner.fileInfo.Size = converted.Size
|
|
||||||
|
|
||||||
if len(converted.ThumbnailData) > 0 {
|
var thumbnailData []byte
|
||||||
thumbnailMXC, thumbnailFileInfo, err := intent.UploadMedia(ctx, t.inner.roomID, converted.ThumbnailData, t.inner.filename, converted.ThumbnailMIMEType)
|
var thumbnailMIMEType string
|
||||||
|
mxc, encryptedFileInfo, err = intent.UploadMediaStream(ctx, t.inner.roomID, int64(t.inner.fileInfo.Size), needStickerConvert, func(file io.Writer) (*bridgev2.FileStreamResult, error) {
|
||||||
|
_, err := io.Copy(file, reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to stream download: %w", err)
|
||||||
|
}
|
||||||
|
var replacementFile string
|
||||||
|
if needStickerConvert {
|
||||||
|
osFile := file.(*os.File)
|
||||||
|
_, err = osFile.Seek(0, io.SeekStart)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Err(err).Msg("failed to upload animated sticker thumbnail to Matrix")
|
return nil, fmt.Errorf("failed to seek to start of file for sticker conversion: %w", err)
|
||||||
|
}
|
||||||
|
var converted *ConvertedSticker
|
||||||
|
if t.inner.fileInfo.MimeType == "video/webm" {
|
||||||
|
converted = t.inner.animatedStickerConfig.convertWebm(ctx, osFile)
|
||||||
} else {
|
} else {
|
||||||
t.inner = t.inner.WithThumbnail(thumbnailMXC, thumbnailFileInfo, &event.FileInfo{
|
t.inner.fileInfo.MimeType = "video/lottie+json"
|
||||||
MimeType: converted.ThumbnailMIMEType,
|
converted = t.inner.animatedStickerConfig.convert(ctx, osFile)
|
||||||
Width: converted.Width,
|
}
|
||||||
Height: converted.Height,
|
if converted != nil {
|
||||||
Size: len(converted.ThumbnailData),
|
replacementFile = converted.NewPath
|
||||||
})
|
t.inner.fileInfo.MimeType = converted.MIMEType
|
||||||
|
t.inner.fileInfo.Width = converted.Width
|
||||||
|
t.inner.fileInfo.Height = converted.Height
|
||||||
|
t.inner.fileInfo.Size = converted.Size
|
||||||
|
thumbnailData = converted.ThumbnailData
|
||||||
|
thumbnailMIMEType = converted.ThumbnailMIMEType
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
mxc, encryptedFileInfo, err = intent.UploadMediaStream(ctx, t.inner.roomID, int64(t.inner.fileInfo.Size), false, func(file io.Writer) (*bridgev2.FileStreamResult, error) {
|
|
||||||
_, err := io.Copy(file, reader)
|
|
||||||
return &bridgev2.FileStreamResult{
|
return &bridgev2.FileStreamResult{
|
||||||
FileName: t.inner.filename,
|
FileName: t.inner.filename,
|
||||||
MimeType: t.inner.fileInfo.MimeType,
|
MimeType: t.inner.fileInfo.MimeType,
|
||||||
|
ReplacementFile: replacementFile,
|
||||||
}, err
|
}, err
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil, nil, fmt.Errorf("failed to upload media to Matrix: %w", err)
|
return "", nil, nil, fmt.Errorf("failed to upload media to Matrix: %w", err)
|
||||||
}
|
}
|
||||||
|
if thumbnailData != nil {
|
||||||
|
thumbnailMXC, thumbnailFileInfo, err := intent.UploadMedia(ctx, t.inner.roomID, thumbnailData, t.inner.filename, thumbnailMIMEType)
|
||||||
|
if err != nil {
|
||||||
|
log.Err(err).Msg("failed to upload animated sticker thumbnail to Matrix")
|
||||||
|
} else {
|
||||||
|
t.inner = t.inner.WithThumbnail(thumbnailMXC, thumbnailFileInfo, &event.FileInfo{
|
||||||
|
MimeType: thumbnailMIMEType,
|
||||||
|
Width: t.inner.fileInfo.Width,
|
||||||
|
Height: t.inner.fileInfo.Height,
|
||||||
|
Size: len(thumbnailData),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If it's an unencrypted file, cache the MXC URI corresponding to the
|
// If it's an unencrypted file, cache the MXC URI corresponding to the
|
||||||
// location ID.
|
// location ID.
|
||||||
@@ -338,7 +347,7 @@ func (t *ReadyTransferer) Stream(ctx context.Context) (r io.Reader, mimeType str
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", 0, err
|
return nil, "", 0, err
|
||||||
}
|
}
|
||||||
if t.inner.fileInfo.MimeType == "" {
|
if t.inner.fileInfo.MimeType == "" || t.inner.fileInfo.MimeType == "application/octet-stream" {
|
||||||
switch storageFileTypeClass.(type) {
|
switch storageFileTypeClass.(type) {
|
||||||
case *tg.StorageFileJpeg:
|
case *tg.StorageFileJpeg:
|
||||||
t.inner.fileInfo.MimeType = "image/jpeg"
|
t.inner.fileInfo.MimeType = "image/jpeg"
|
||||||
@@ -349,7 +358,7 @@ func (t *ReadyTransferer) Stream(ctx context.Context) (r io.Reader, mimeType str
|
|||||||
case *tg.StorageFilePdf:
|
case *tg.StorageFilePdf:
|
||||||
t.inner.fileInfo.MimeType = "application/pdf"
|
t.inner.fileInfo.MimeType = "application/pdf"
|
||||||
case *tg.StorageFileMp3:
|
case *tg.StorageFileMp3:
|
||||||
t.inner.fileInfo.MimeType = "audio/mp3"
|
t.inner.fileInfo.MimeType = "audio/mpeg"
|
||||||
case *tg.StorageFileMov:
|
case *tg.StorageFileMov:
|
||||||
t.inner.fileInfo.MimeType = "video/quicktime"
|
t.inner.fileInfo.MimeType = "video/quicktime"
|
||||||
case *tg.StorageFileMp4:
|
case *tg.StorageFileMp4:
|
||||||
@@ -361,24 +370,58 @@ func (t *ReadyTransferer) Stream(ctx context.Context) (r io.Reader, mimeType str
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return r, t.inner.fileInfo.MimeType, t.inner.fileInfo.Size, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *ReadyTransferer) ToDirectMediaResponse(ctx context.Context) (mediaproxy.GetMediaResponse, error) {
|
||||||
|
if t == nil {
|
||||||
|
return nil, fmt.Errorf("invalid direct media request")
|
||||||
|
}
|
||||||
|
log := zerolog.Ctx(ctx)
|
||||||
|
r, mimeType, size, err := t.Stream(ctx)
|
||||||
|
if err != nil {
|
||||||
|
log.Err(err).Msg("Failed to download media")
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
log.Debug().
|
||||||
|
Str("mime_type", mimeType).
|
||||||
|
Int("size", size).
|
||||||
|
Msg("Started downloading media successfully")
|
||||||
|
|
||||||
if t.inner.animatedStickerConfig != nil {
|
if t.inner.animatedStickerConfig != nil {
|
||||||
data, err := io.ReadAll(r)
|
return &mediaproxy.GetMediaResponseFile{
|
||||||
if err != nil {
|
Callback: func(w *os.File) (*mediaproxy.FileMeta, error) {
|
||||||
return nil, "", 0, fmt.Errorf("failed to read animated sticker data: %w", err)
|
_, err = io.Copy(w, r)
|
||||||
} else if detected := http.DetectContentType(data); detected == "application/x-tgsticker" || detected == "application/x-gzip" {
|
if err != nil {
|
||||||
if unzipped, err := gnuzip.MaybeGUnzip(data); err != nil {
|
return nil, fmt.Errorf("failed to write animated sticker data to file: %w", err)
|
||||||
zerolog.Ctx(ctx).Err(err).Msg("failed to unzip animated sticker")
|
}
|
||||||
} else {
|
_, err = w.Seek(0, io.SeekStart)
|
||||||
converted := t.inner.animatedStickerConfig.convert(ctx, unzipped)
|
if err != nil {
|
||||||
t.inner.fileInfo.MimeType = converted.MIMEType
|
return nil, fmt.Errorf("failed to seek to start of file for sticker conversion: %w", err)
|
||||||
t.inner.fileInfo.Size = converted.Size
|
}
|
||||||
return converted.DataWriter, t.inner.fileInfo.MimeType, t.inner.fileInfo.Size, nil
|
var converted *ConvertedSticker
|
||||||
}
|
if t.inner.fileInfo.MimeType == "video/webm" {
|
||||||
}
|
converted = t.inner.animatedStickerConfig.convertWebm(ctx, w)
|
||||||
return bytes.NewReader(data), t.inner.fileInfo.MimeType, t.inner.fileInfo.Size, nil
|
} else {
|
||||||
|
t.inner.fileInfo.MimeType = "video/lottie+json"
|
||||||
|
converted = t.inner.animatedStickerConfig.convert(ctx, w)
|
||||||
|
}
|
||||||
|
if converted == nil {
|
||||||
|
return &mediaproxy.FileMeta{ContentType: t.inner.fileInfo.MimeType}, nil
|
||||||
|
}
|
||||||
|
return &mediaproxy.FileMeta{
|
||||||
|
ContentType: converted.MIMEType,
|
||||||
|
ReplacementFile: converted.NewPath,
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return r, t.inner.fileInfo.MimeType, t.inner.fileInfo.Size, nil
|
return &mediaproxy.GetMediaResponseData{
|
||||||
|
Reader: io.NopCloser(r),
|
||||||
|
ContentType: mimeType,
|
||||||
|
ContentLength: int64(size),
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DownloadBytes downloads the media from Telegram to a byte buffer.
|
// DownloadBytes downloads the media from Telegram to a byte buffer.
|
||||||
|
|||||||
Reference in New Issue
Block a user