gmessages/libgm/media_processor.go

148 lines
3.7 KiB
Go
Raw Normal View History

2023-06-30 11:05:33 +00:00
package libgm
2023-06-30 09:54:08 +00:00
import (
"bytes"
2023-07-15 17:43:28 +00:00
"encoding/base64"
2023-06-30 09:54:08 +00:00
"errors"
"io"
"net/http"
"strconv"
2023-07-15 17:43:28 +00:00
"google.golang.org/protobuf/proto"
2023-06-30 09:54:08 +00:00
"go.mau.fi/mautrix-gmessages/libgm/binary"
"go.mau.fi/mautrix-gmessages/libgm/payload"
2023-06-30 09:54:08 +00:00
"go.mau.fi/mautrix-gmessages/libgm/util"
)
type StartGoogleUpload struct {
2023-06-30 13:26:46 +00:00
UploadID string
UploadURL string
2023-06-30 09:54:08 +00:00
UploadStatus string
ChunkGranularity int64
2023-06-30 13:26:46 +00:00
ControlURL string
MimeType string
2023-06-30 09:54:08 +00:00
EncryptedMediaBytes []byte
}
type MediaUpload struct {
2023-06-30 13:26:46 +00:00
MediaID string
2023-06-30 09:54:08 +00:00
MediaNumber int64
}
var (
errStartUploadMedia = errors.New("failed to start uploading media")
errFinalizeUploadMedia = errors.New("failed to finalize uploading media")
)
func (c *Client) FinalizeUploadMedia(upload *StartGoogleUpload) (*MediaUpload, error) {
encryptedImageSize := strconv.Itoa(len(upload.EncryptedMediaBytes))
finalizeUploadHeaders := util.NewMediaUploadHeaders(encryptedImageSize, "upload, finalize", "0", upload.MimeType, "")
2023-06-30 13:26:46 +00:00
req, reqErr := http.NewRequest("POST", upload.UploadURL, bytes.NewBuffer(upload.EncryptedMediaBytes))
2023-06-30 09:54:08 +00:00
if reqErr != nil {
return nil, reqErr
}
req.Header = *finalizeUploadHeaders
res, resErr := c.http.Do(req)
if resErr != nil {
2023-07-09 20:32:19 +00:00
panic(resErr)
2023-06-30 09:54:08 +00:00
}
statusCode := res.StatusCode
if statusCode != 200 {
return nil, errFinalizeUploadMedia
}
defer res.Body.Close()
rHeaders := res.Header
2023-07-15 17:43:28 +00:00
googleResponse, err3 := io.ReadAll(base64.NewDecoder(base64.StdEncoding, res.Body))
2023-06-30 09:54:08 +00:00
if err3 != nil {
return nil, err3
}
uploadStatus := rHeaders.Get("x-goog-upload-status")
2023-07-09 20:32:19 +00:00
c.Logger.Debug().Str("upload_status", uploadStatus).Msg("Upload complete")
2023-06-30 09:54:08 +00:00
2023-06-30 13:26:46 +00:00
mediaIDs := &binary.UploadMediaResponse{}
2023-07-15 17:43:28 +00:00
err3 = proto.Unmarshal(googleResponse, mediaIDs)
2023-06-30 09:54:08 +00:00
if err3 != nil {
return nil, err3
}
return &MediaUpload{
2023-06-30 13:26:46 +00:00
MediaID: mediaIDs.Media.MediaID,
MediaNumber: mediaIDs.Media.MediaNumber,
2023-06-30 09:54:08 +00:00
}, nil
}
func (c *Client) StartUploadMedia(encryptedImageBytes []byte, mime string) (*StartGoogleUpload, error) {
2023-06-30 09:54:08 +00:00
encryptedImageSize := strconv.Itoa(len(encryptedImageBytes))
startUploadHeaders := util.NewMediaUploadHeaders(encryptedImageSize, "start", "", mime, "resumable")
2023-06-30 09:54:08 +00:00
startUploadPayload, buildPayloadErr := c.buildStartUploadPayload()
if buildPayloadErr != nil {
return nil, buildPayloadErr
}
2023-07-15 23:21:53 +00:00
req, reqErr := http.NewRequest("POST", util.UploadMediaURL, bytes.NewBuffer([]byte(startUploadPayload)))
2023-06-30 09:54:08 +00:00
if reqErr != nil {
return nil, reqErr
}
req.Header = *startUploadHeaders
res, resErr := c.http.Do(req)
if resErr != nil {
2023-07-09 20:32:19 +00:00
panic(resErr)
2023-06-30 09:54:08 +00:00
}
statusCode := res.StatusCode
if statusCode != 200 {
return nil, errStartUploadMedia
}
rHeaders := res.Header
chunkGranularity, convertErr := strconv.Atoi(rHeaders.Get("x-goog-upload-chunk-granularity"))
if convertErr != nil {
return nil, convertErr
}
uploadResponse := &StartGoogleUpload{
2023-06-30 13:26:46 +00:00
UploadID: rHeaders.Get("x-guploader-uploadid"),
UploadURL: rHeaders.Get("x-goog-upload-url"),
2023-06-30 09:54:08 +00:00
UploadStatus: rHeaders.Get("x-goog-upload-status"),
ChunkGranularity: int64(chunkGranularity),
2023-06-30 13:26:46 +00:00
ControlURL: rHeaders.Get("x-goog-upload-control-url"),
MimeType: mime,
2023-06-30 09:54:08 +00:00
EncryptedMediaBytes: encryptedImageBytes,
}
return uploadResponse, nil
}
func (c *Client) buildStartUploadPayload() (string, error) {
requestID := util.RandomUUIDv4()
2023-06-30 09:54:08 +00:00
protoData := &binary.StartMediaUploadPayload{
ImageType: 1,
AuthData: &binary.AuthMessage{
RequestID: requestID,
TachyonAuthToken: c.authData.TachyonAuthToken,
ConfigVersion: payload.ConfigMessage,
2023-06-30 09:54:08 +00:00
},
Mobile: c.authData.DevicePair.Mobile,
2023-06-30 09:54:08 +00:00
}
2023-07-15 17:43:28 +00:00
protoDataBytes, err := proto.Marshal(protoData)
if err != nil {
return "", err
2023-06-30 09:54:08 +00:00
}
2023-07-15 17:43:28 +00:00
protoDataEncoded := base64.StdEncoding.EncodeToString(protoDataBytes)
2023-06-30 09:54:08 +00:00
return protoDataEncoded, nil
}