gmessages/libgm/media_processor.go
2023-06-30 14:05:33 +03:00

167 lines
4 KiB
Go

package libgm
import (
"bytes"
"encoding/base64"
"errors"
"io"
"log"
"net/http"
"strconv"
"go.mau.fi/mautrix-gmessages/libgm/binary"
"go.mau.fi/mautrix-gmessages/libgm/crypto"
"go.mau.fi/mautrix-gmessages/libgm/util"
)
type StartGoogleUpload struct {
UploadId string
UploadUrl string
UploadStatus string
ChunkGranularity int64
ControlUrl string
Image *Image
EncryptedMediaBytes []byte
}
type MediaUpload struct {
MediaId string
MediaNumber int64
Image *Image
}
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) {
imageType := upload.Image.GetImageType()
encryptedImageSize := strconv.Itoa(len(upload.EncryptedMediaBytes))
log.Println("EncryptedImageSize:", encryptedImageSize)
finalizeUploadHeaders := util.NewMediaUploadHeaders(encryptedImageSize, "upload, finalize", "0", imageType.Format, "")
req, reqErr := http.NewRequest("POST", upload.UploadUrl, bytes.NewBuffer(upload.EncryptedMediaBytes))
if reqErr != nil {
return nil, reqErr
}
req.Header = *finalizeUploadHeaders
res, resErr := c.http.Do(req)
if resErr != nil {
log.Fatal(resErr)
}
statusCode := res.StatusCode
if statusCode != 200 {
return nil, errFinalizeUploadMedia
}
defer res.Body.Close()
rHeaders := res.Header
googleResponse, err3 := io.ReadAll(res.Body)
if err3 != nil {
return nil, err3
}
uploadStatus := rHeaders.Get("x-goog-upload-status")
log.Println("Upload Status: ", uploadStatus)
mediaIds := &binary.UploadMediaResponse{}
err3 = crypto.DecodeAndEncodeB64(string(googleResponse), mediaIds)
if err3 != nil {
return nil, err3
}
return &MediaUpload{
MediaId: mediaIds.Media.MediaId,
MediaNumber: mediaIds.Media.MediaNumber,
Image: upload.Image,
}, nil
}
func (c *Client) StartUploadMedia(image *Image) (*StartGoogleUpload, error) {
imageType := image.GetImageType()
encryptedImageBytes, encryptErr := image.GetEncryptedBytes()
if encryptErr != nil {
return nil, encryptErr
}
encryptedImageSize := strconv.Itoa(len(encryptedImageBytes))
startUploadHeaders := util.NewMediaUploadHeaders(encryptedImageSize, "start", "", imageType.Format, "resumable")
startUploadPayload, buildPayloadErr := c.buildStartUploadPayload()
if buildPayloadErr != nil {
return nil, buildPayloadErr
}
req, reqErr := http.NewRequest("POST", util.UPLOAD_MEDIA, bytes.NewBuffer([]byte(startUploadPayload)))
if reqErr != nil {
return nil, reqErr
}
req.Header = *startUploadHeaders
res, resErr := c.http.Do(req)
if resErr != nil {
log.Fatal(resErr)
}
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{
UploadId: rHeaders.Get("x-guploader-uploadid"),
UploadUrl: rHeaders.Get("x-goog-upload-url"),
UploadStatus: rHeaders.Get("x-goog-upload-status"),
ChunkGranularity: int64(chunkGranularity),
ControlUrl: rHeaders.Get("x-goog-upload-control-url"),
Image: image,
EncryptedMediaBytes: encryptedImageBytes,
}
return uploadResponse, nil
}
func (c *Client) buildStartUploadPayload() (string, error) {
decodedRpcKey, err := base64.StdEncoding.DecodeString(c.rpcKey)
if err != nil {
return "", err
}
requestId := util.RandomUUIDv4()
protoData := &binary.StartMediaUploadPayload{
ImageType: 1,
AuthData: &binary.AuthMessageBytes{
RequestId: requestId,
RpcKey: decodedRpcKey,
Date: &binary.Date{
Year: 2023,
Seq1: 6,
Seq2: 8,
Seq3: 4,
Seq4: 6,
},
},
Mobile: c.devicePair.Mobile,
}
protoDataEncoded, protoEncodeErr := crypto.EncodeProtoB64(protoData)
if protoEncodeErr != nil {
return "", protoEncodeErr
}
return protoDataEncoded, nil
}