mirror of
https://github.com/Mrs4s/MiraiGo.git
synced 2025-05-04 11:07:40 +08:00
feature short video upload & sending.
This commit is contained in:
parent
72521dec9b
commit
7605dc4635
@ -16,7 +16,6 @@ import (
|
||||
"github.com/Mrs4s/MiraiGo/client/pb/cmd0x352"
|
||||
"github.com/Mrs4s/MiraiGo/client/pb/msg"
|
||||
"github.com/Mrs4s/MiraiGo/client/pb/oidb"
|
||||
"github.com/Mrs4s/MiraiGo/client/pb/pttcenter"
|
||||
"github.com/Mrs4s/MiraiGo/client/pb/structmsg"
|
||||
"github.com/Mrs4s/MiraiGo/message"
|
||||
"github.com/Mrs4s/MiraiGo/protocol/crypto"
|
||||
@ -1010,31 +1009,6 @@ func (c *QQClient) buildImageOcrRequestPacket(url, md5 string, size, weight, hei
|
||||
return seq, packet
|
||||
}
|
||||
|
||||
// PttCenterSvr.ShortVideoDownReq
|
||||
func (c *QQClient) buildPttShortVideoDownReqPacket(uuid, md5 []byte) (uint16, []byte) {
|
||||
seq := c.nextSeq()
|
||||
body := &pttcenter.ShortVideoReqBody{
|
||||
Cmd: 400,
|
||||
Seq: int32(seq),
|
||||
PttShortVideoDownloadReq: &pttcenter.ShortVideoDownloadReq{
|
||||
FromUin: c.Uin,
|
||||
ToUin: c.Uin,
|
||||
ChatType: 1,
|
||||
ClientType: 7,
|
||||
FileId: string(uuid),
|
||||
GroupCode: 1,
|
||||
FileMd5: md5,
|
||||
BusinessType: 1,
|
||||
FileType: 2,
|
||||
DownType: 2,
|
||||
SceneType: 2,
|
||||
},
|
||||
}
|
||||
payload, _ := proto.Marshal(body)
|
||||
packet := packets.BuildUniPacket(c.Uin, seq, "PttCenterSvr.ShortVideoDownReq", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload)
|
||||
return seq, packet
|
||||
}
|
||||
|
||||
// LightAppSvc.mini_app_info.GetAppInfoById
|
||||
func (c *QQClient) buildAppInfoRequestPacket(id string) (uint16, []byte) {
|
||||
seq := c.nextSeq()
|
||||
|
@ -139,7 +139,6 @@ var decoders = map[string]func(*QQClient, uint16, []byte) (interface{}, error){
|
||||
"OidbSvc.0xd79": decodeWordSegmentation,
|
||||
"OidbSvc.0x990": decodeTranslateResponse,
|
||||
"SummaryCard.ReqSummaryCard": decodeSummaryCardResponse,
|
||||
"PttCenterSvr.ShortVideoDownReq": decodePttShortVideoDownResponse,
|
||||
"LightAppSvc.mini_app_info.GetAppInfoById": decodeAppInfoResponse,
|
||||
"PttCenterSvr.pb_pttCenter_CMD_REQ_APPLY_UPLOAD-500": decodePrivatePttStoreResponse,
|
||||
}
|
||||
@ -421,14 +420,6 @@ func (c *QQClient) GetFriendList() (*FriendListResponse, error) {
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func (c *QQClient) GetShortVideoUrl(uuid, md5 []byte) string {
|
||||
i, err := c.sendAndWait(c.buildPttShortVideoDownReqPacket(uuid, md5))
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return i.(string)
|
||||
}
|
||||
|
||||
func (c *QQClient) SendPrivateMessage(target int64, m *message.SendingMessage) *message.PrivateMessage {
|
||||
mr := int32(rand.Uint32())
|
||||
seq := c.nextFriendSeq()
|
||||
|
@ -12,7 +12,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/Mrs4s/MiraiGo/client/pb/notify"
|
||||
"github.com/Mrs4s/MiraiGo/client/pb/pttcenter"
|
||||
"github.com/Mrs4s/MiraiGo/client/pb/qweb"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
@ -992,18 +991,6 @@ func decodeImageOcrResponse(_ *QQClient, _ uint16, payload []byte) (interface{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// PttCenterSvr.ShortVideoDownReq
|
||||
func decodePttShortVideoDownResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, error) {
|
||||
rsp := pttcenter.ShortVideoRspBody{}
|
||||
if err := proto.Unmarshal(payload, &rsp); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
|
||||
}
|
||||
if rsp.PttShortVideoDownloadRsp == nil || rsp.PttShortVideoDownloadRsp.DownloadAddr == nil {
|
||||
return nil, errors.New("resp error")
|
||||
}
|
||||
return rsp.PttShortVideoDownloadRsp.DownloadAddr.Host[0] + rsp.PttShortVideoDownloadRsp.DownloadAddr.UrlArgs, nil
|
||||
}
|
||||
|
||||
// LightAppSvc.mini_app_info.GetAppInfoById
|
||||
func decodeAppInfoResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, error) {
|
||||
pkg := qweb.QWebRsp{}
|
||||
|
@ -97,11 +97,16 @@ func (c *QQClient) highwayUploadStream(ip uint32, port int, updKey []byte, strea
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *QQClient) highwayUploadByBDH(stream io.ReadSeeker, cmdId int32, ticket, ext []byte) ([]byte, error) {
|
||||
// TODO: encrypted upload support.
|
||||
func (c *QQClient) highwayUploadByBDH(stream io.ReadSeeker, cmdId int32, ticket, ext []byte, encrypt bool) ([]byte, error) {
|
||||
if len(c.srvSsoAddrs) == 0 {
|
||||
return nil, errors.New("srv addrs not found. maybe miss some packet?")
|
||||
}
|
||||
if encrypt {
|
||||
if c.highwaySession == nil || len(c.highwaySession.SessionKey) == 0 {
|
||||
return nil, errors.New("session key not found. maybe miss some packet?")
|
||||
}
|
||||
ext = binary.NewTeaCipher(c.highwaySession.SessionKey).Encrypt(ext)
|
||||
}
|
||||
h := md5.New()
|
||||
length, _ := io.Copy(h, stream)
|
||||
fh := h.Sum(nil)
|
||||
@ -194,7 +199,7 @@ func (c *QQClient) highwayUploadFileMultiThreadingByBDH(path string, cmdId int32
|
||||
return nil, errors.Wrap(err, "open file error")
|
||||
}
|
||||
if stat.Size() < 1024*1024*3 {
|
||||
return c.highwayUploadByBDH(file, cmdId, ticket, ext)
|
||||
return c.highwayUploadByBDH(file, cmdId, ticket, ext, false)
|
||||
}
|
||||
type BlockMetaData struct {
|
||||
Id int
|
||||
|
@ -44,7 +44,7 @@ func (c *QQClient) UploadGroupImage(groupCode int64, img io.ReadSeeker) (*messag
|
||||
c.srvSsoAddrs = append(c.srvSsoAddrs, fmt.Sprintf("%v:%v", binary.UInt32ToIPV4Address(uint32(addr)), rsp.UploadPort[i]))
|
||||
}
|
||||
}
|
||||
if _, err = c.highwayUploadByBDH(img, 2, rsp.UploadKey, EmptyBytes); err == nil {
|
||||
if _, err = c.highwayUploadByBDH(img, 2, rsp.UploadKey, EmptyBytes, true); err == nil {
|
||||
goto ok
|
||||
}
|
||||
return nil, errors.New("upload failed")
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -5,15 +5,29 @@ option go_package = ".;pttcenter";
|
||||
message ShortVideoReqBody {
|
||||
int32 cmd = 1;
|
||||
int32 seq = 2;
|
||||
ShortVideoUploadReq pttShortVideoUploadReq = 3;
|
||||
ShortVideoDownloadReq pttShortVideoDownloadReq = 4;
|
||||
repeated ShortVideoExtensionReq extensionReq = 100;
|
||||
}
|
||||
|
||||
message ShortVideoRspBody {
|
||||
int32 cmd = 1;
|
||||
int32 seq = 2;
|
||||
ShortVideoUploadRsp pttShortVideoUploadRsp = 3;
|
||||
ShortVideoDownloadRsp pttShortVideoDownloadRsp = 4;
|
||||
}
|
||||
|
||||
message ShortVideoUploadReq {
|
||||
int64 fromUin = 1;
|
||||
int64 toUin = 2;
|
||||
int32 chatType = 3;
|
||||
int32 clientType = 4;
|
||||
ShortVideoFileInfo info = 5;
|
||||
int64 groupCode = 6;
|
||||
int32 agentType = 7;
|
||||
int32 businessType = 8;
|
||||
int32 supportLargeSize = 20;
|
||||
}
|
||||
message ShortVideoDownloadReq {
|
||||
int64 fromUin = 1;
|
||||
int64 toUin = 2;
|
||||
@ -42,6 +56,36 @@ message ShortVideoDownloadRsp {
|
||||
bytes encryptKey = 10;
|
||||
}
|
||||
|
||||
message ShortVideoUploadRsp {
|
||||
int32 retCode = 1;
|
||||
string retMsg = 2;
|
||||
repeated ShortVideoIpList sameAreaOutAddr = 3;
|
||||
repeated ShortVideoIpList diffAreaOutAddr = 4;
|
||||
string fileId = 5;
|
||||
bytes uKey = 6;
|
||||
int32 fileExists = 7;
|
||||
repeated ShortVideoIpList sameAreaInnerAddr = 8;
|
||||
repeated ShortVideoIpList diffAreaInnerAddr = 9;
|
||||
repeated DataHole dataHole = 10;
|
||||
}
|
||||
|
||||
message ShortVideoFileInfo {
|
||||
string fileName = 1;
|
||||
bytes fileMd5 = 2;
|
||||
bytes thumbFileMd5 = 3;
|
||||
int64 fileSize = 4;
|
||||
int32 fileResLength = 5;
|
||||
int32 fileResWidth = 6;
|
||||
int32 fileFormat = 7;
|
||||
int32 fileTime = 8;
|
||||
int64 thumbFileSize = 9;
|
||||
}
|
||||
|
||||
message DataHole {
|
||||
int64 begin = 1;
|
||||
int64 end = 2;
|
||||
}
|
||||
|
||||
message ShortVideoIpList {
|
||||
int32 ip = 1;
|
||||
int32 port = 2;
|
||||
@ -51,4 +95,9 @@ message ShortVideoAddr {
|
||||
repeated string host = 10;
|
||||
string urlArgs = 11;
|
||||
//repeated string domain = 13;
|
||||
}
|
||||
|
||||
message ShortVideoExtensionReq {
|
||||
int32 subBusiType = 1;
|
||||
int32 userCnt = 2;
|
||||
}
|
174
client/ptt.go
174
client/ptt.go
@ -3,19 +3,23 @@ package client
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"io"
|
||||
|
||||
"github.com/Mrs4s/MiraiGo/binary"
|
||||
"github.com/Mrs4s/MiraiGo/client/pb"
|
||||
"github.com/Mrs4s/MiraiGo/client/pb/cmd0x346"
|
||||
"github.com/Mrs4s/MiraiGo/client/pb/msg"
|
||||
"github.com/Mrs4s/MiraiGo/client/pb/pttcenter"
|
||||
"github.com/Mrs4s/MiraiGo/message"
|
||||
"github.com/Mrs4s/MiraiGo/protocol/packets"
|
||||
"github.com/Mrs4s/MiraiGo/utils"
|
||||
"github.com/pkg/errors"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"io"
|
||||
)
|
||||
|
||||
// 语音相关处理逻辑
|
||||
func init() {
|
||||
decoders["PttCenterSvr.ShortVideoDownReq"] = decodePttShortVideoDownResponse
|
||||
decoders["PttCenterSvr.GroupShortVideoUpReq"] = decodeGroupShortVideoUploadResponse
|
||||
}
|
||||
|
||||
// UploadGroupPtt 将语音数据使用群语音通道上传到服务器, 返回 message.GroupVoiceElement 可直接发送
|
||||
func (c *QQClient) UploadGroupPtt(groupCode int64, voice io.ReadSeeker) (*message.GroupVoiceElement, error) {
|
||||
@ -24,7 +28,7 @@ func (c *QQClient) UploadGroupPtt(groupCode int64, voice io.ReadSeeker) (*messag
|
||||
fh := h.Sum(nil)
|
||||
_, _ = voice.Seek(0, io.SeekStart)
|
||||
ext := c.buildGroupPttStoreBDHExt(groupCode, fh[:], int32(length), 0, int32(length))
|
||||
rsp, err := c.highwayUploadByBDH(voice, 29, c.highwaySession.SigSession, ext)
|
||||
rsp, err := c.highwayUploadByBDH(voice, 29, c.highwaySession.SigSession, ext, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -38,29 +42,6 @@ func (c *QQClient) UploadGroupPtt(groupCode int64, voice io.ReadSeeker) (*messag
|
||||
if len(pkt.MsgTryUpPttRsp) == 0 {
|
||||
return nil, errors.New("miss try up rsp")
|
||||
}
|
||||
/*
|
||||
seq, pkt := c.buildGroupPttStorePacket(groupCode, h[:], int32(len(voice)), 0, int32(len(voice)))
|
||||
r, err := c.sendAndWait(seq, pkt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rsp := r.(pttUploadResponse)
|
||||
if rsp.ResultCode != 0 {
|
||||
return nil, errors.New(rsp.Message)
|
||||
}
|
||||
if rsp.IsExists {
|
||||
goto ok
|
||||
}
|
||||
for i, ip := range rsp.UploadIp {
|
||||
err := c.uploadPtt(ip, rsp.UploadPort[i], rsp.UploadKey, rsp.FileKey, voice, h[:])
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
goto ok
|
||||
}
|
||||
return nil, errors.New("upload failed")
|
||||
ok:
|
||||
*/
|
||||
return &message.GroupVoiceElement{
|
||||
Ptt: &msg.Ptt{
|
||||
FileType: proto.Int32(4),
|
||||
@ -107,6 +88,52 @@ ok:
|
||||
}}, nil
|
||||
}
|
||||
|
||||
func (c *QQClient) UploadGroupShortVideo(groupCode int64, video, thumb io.ReadSeeker) (*message.ShortVideoElement, error) {
|
||||
videoHash, videoLen := utils.GetMd5AndLength(video)
|
||||
thumbHash, thumbLen := utils.GetMd5AndLength(thumb)
|
||||
i, err := c.sendAndWait(c.buildPttGroupShortVideoUploadReqPacket(videoHash, thumbHash, groupCode, videoLen, thumbLen))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "upload req error")
|
||||
}
|
||||
rsp := i.(*pttcenter.ShortVideoUploadRsp)
|
||||
if rsp.FileExists == 1 {
|
||||
return &message.ShortVideoElement{
|
||||
Uuid: []byte(rsp.FileId),
|
||||
Size: int32(videoLen),
|
||||
ThumbSize: int32(thumbLen),
|
||||
Md5: videoHash,
|
||||
ThumbMd5: thumbHash,
|
||||
}, nil
|
||||
}
|
||||
ext, _ := proto.Marshal(c.buildPttGroupShortVideoProto(videoHash, thumbHash, groupCode, videoLen, thumbLen).PttShortVideoUploadReq)
|
||||
hwRsp, err := c.highwayUploadByBDH(utils.MultiReadSeeker(thumb, video), 25, c.highwaySession.SigSession, ext, true)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "upload video file error")
|
||||
}
|
||||
if len(hwRsp) == 0 {
|
||||
return nil, errors.New("resp is empty")
|
||||
}
|
||||
rsp = &pttcenter.ShortVideoUploadRsp{}
|
||||
if err = proto.Unmarshal(hwRsp, rsp); err != nil {
|
||||
return nil, errors.Wrap(err, "decode error")
|
||||
}
|
||||
return &message.ShortVideoElement{
|
||||
Uuid: []byte(rsp.FileId),
|
||||
Size: int32(videoLen),
|
||||
ThumbSize: int32(thumbLen),
|
||||
Md5: videoHash,
|
||||
ThumbMd5: thumbHash,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *QQClient) GetShortVideoUrl(uuid, md5 []byte) string {
|
||||
i, err := c.sendAndWait(c.buildPttShortVideoDownReqPacket(uuid, md5))
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return i.(string)
|
||||
}
|
||||
|
||||
// PttStore.GroupPttUp
|
||||
func (c *QQClient) buildGroupPttStorePacket(groupCode int64, md5 []byte, size, codec, voiceLength int32) (uint16, []byte) {
|
||||
seq := c.nextSeq()
|
||||
@ -142,6 +169,72 @@ func (c *QQClient) buildGroupPttStoreBDHExt(groupCode int64, md5 []byte, size, c
|
||||
return payload
|
||||
}
|
||||
|
||||
// PttCenterSvr.ShortVideoDownReq
|
||||
func (c *QQClient) buildPttShortVideoDownReqPacket(uuid, md5 []byte) (uint16, []byte) {
|
||||
seq := c.nextSeq()
|
||||
body := &pttcenter.ShortVideoReqBody{
|
||||
Cmd: 400,
|
||||
Seq: int32(seq),
|
||||
PttShortVideoDownloadReq: &pttcenter.ShortVideoDownloadReq{
|
||||
FromUin: c.Uin,
|
||||
ToUin: c.Uin,
|
||||
ChatType: 1,
|
||||
ClientType: 7,
|
||||
FileId: string(uuid),
|
||||
GroupCode: 1,
|
||||
FileMd5: md5,
|
||||
BusinessType: 1,
|
||||
FileType: 2,
|
||||
DownType: 2,
|
||||
SceneType: 2,
|
||||
},
|
||||
}
|
||||
payload, _ := proto.Marshal(body)
|
||||
packet := packets.BuildUniPacket(c.Uin, seq, "PttCenterSvr.ShortVideoDownReq", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload)
|
||||
return seq, packet
|
||||
}
|
||||
|
||||
func (c *QQClient) buildPttGroupShortVideoProto(videoHash, thumbHash []byte, toUin, videoSize, thumbSize int64) *pttcenter.ShortVideoReqBody {
|
||||
seq := c.nextSeq()
|
||||
return &pttcenter.ShortVideoReqBody{
|
||||
Cmd: 300,
|
||||
Seq: int32(seq),
|
||||
PttShortVideoUploadReq: &pttcenter.ShortVideoUploadReq{
|
||||
FromUin: c.Uin,
|
||||
ToUin: toUin,
|
||||
ChatType: 1,
|
||||
ClientType: 2,
|
||||
Info: &pttcenter.ShortVideoFileInfo{
|
||||
FileName: hex.EncodeToString(videoHash) + ".mp4",
|
||||
FileMd5: videoHash,
|
||||
ThumbFileMd5: thumbHash,
|
||||
FileSize: videoSize,
|
||||
FileResLength: 1280,
|
||||
FileResWidth: 720,
|
||||
FileFormat: 3,
|
||||
FileTime: 120,
|
||||
ThumbFileSize: thumbSize,
|
||||
},
|
||||
GroupCode: toUin,
|
||||
SupportLargeSize: 1,
|
||||
},
|
||||
ExtensionReq: []*pttcenter.ShortVideoExtensionReq{
|
||||
{
|
||||
SubBusiType: 0,
|
||||
UserCnt: 1,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// PttCenterSvr.GroupShortVideoUpReq
|
||||
func (c *QQClient) buildPttGroupShortVideoUploadReqPacket(videoHash, thumbHash []byte, toUin, videoSize, thumbSize int64) (uint16, []byte) {
|
||||
seq := c.nextSeq()
|
||||
payload, _ := proto.Marshal(c.buildPttGroupShortVideoProto(videoHash, thumbHash, toUin, videoSize, thumbSize))
|
||||
packet := packets.BuildUniPacket(c.Uin, seq, "PttCenterSvr.GroupShortVideoUpReq", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload)
|
||||
return seq, packet
|
||||
}
|
||||
|
||||
// PttStore.GroupPttUp
|
||||
func decodeGroupPttStoreResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, error) {
|
||||
pkt := pb.D388RespBody{}
|
||||
@ -230,3 +323,30 @@ func decodePrivatePttStoreResponse(c *QQClient, _ uint16, payload []byte) (inter
|
||||
FileKey: rsp.ApplyUploadRsp.Uuid,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// PttCenterSvr.ShortVideoDownReq
|
||||
func decodePttShortVideoDownResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, error) {
|
||||
rsp := pttcenter.ShortVideoRspBody{}
|
||||
if err := proto.Unmarshal(payload, &rsp); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
|
||||
}
|
||||
if rsp.PttShortVideoDownloadRsp == nil || rsp.PttShortVideoDownloadRsp.DownloadAddr == nil {
|
||||
return nil, errors.New("resp error")
|
||||
}
|
||||
return rsp.PttShortVideoDownloadRsp.DownloadAddr.Host[0] + rsp.PttShortVideoDownloadRsp.DownloadAddr.UrlArgs, nil
|
||||
}
|
||||
|
||||
// PttCenterSvr.GroupShortVideoUpReq
|
||||
func decodeGroupShortVideoUploadResponse(_ *QQClient, _ uint16, payload []byte) (interface{}, error) {
|
||||
rsp := pttcenter.ShortVideoRspBody{}
|
||||
if err := proto.Unmarshal(payload, &rsp); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
|
||||
}
|
||||
if rsp.PttShortVideoUploadRsp == nil {
|
||||
return nil, errors.New("resp error")
|
||||
}
|
||||
if rsp.PttShortVideoUploadRsp.RetCode != 0 {
|
||||
return nil, errors.Errorf("ret code error: %v", rsp.PttShortVideoUploadRsp.RetCode)
|
||||
}
|
||||
return rsp.PttShortVideoUploadRsp, nil
|
||||
}
|
||||
|
@ -88,11 +88,13 @@ type ReplyElement struct {
|
||||
}
|
||||
|
||||
type ShortVideoElement struct {
|
||||
Name string
|
||||
Uuid []byte
|
||||
Size int32
|
||||
Md5 []byte
|
||||
Url string
|
||||
Name string
|
||||
Uuid []byte
|
||||
Size int32
|
||||
ThumbSize int32
|
||||
Md5 []byte
|
||||
ThumbMd5 []byte
|
||||
Url string
|
||||
}
|
||||
|
||||
type ServiceElement struct {
|
||||
|
@ -1,6 +1,7 @@
|
||||
package message
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"github.com/Mrs4s/MiraiGo/binary"
|
||||
"github.com/Mrs4s/MiraiGo/client/pb/msg"
|
||||
"github.com/golang/protobuf/proto"
|
||||
@ -256,3 +257,32 @@ func (e *GroupShowPicElement) Pack() (r []*msg.Elem) {
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
func (e *ShortVideoElement) Pack() (r []*msg.Elem) {
|
||||
r = append(r, &msg.Elem{
|
||||
Text: &msg.Text{
|
||||
Str: proto.String("你的QQ暂不支持查看视频短片,请期待后续版本。"),
|
||||
},
|
||||
})
|
||||
r = append(r, &msg.Elem{
|
||||
VideoFile: &msg.VideoFile{
|
||||
FileUuid: e.Uuid,
|
||||
FileMd5: e.Md5,
|
||||
FileName: []byte(hex.EncodeToString(e.Md5) + ".mp4"),
|
||||
FileFormat: proto.Int32(3),
|
||||
FileTime: proto.Int32(10),
|
||||
FileSize: proto.Int32(e.Size),
|
||||
ThumbWidth: proto.Int32(1280),
|
||||
ThumbHeight: proto.Int32(720),
|
||||
ThumbFileMd5: e.ThumbMd5,
|
||||
ThumbFileSize: proto.Int32(e.ThumbSize),
|
||||
BusiType: proto.Int32(0),
|
||||
FromChatType: proto.Int32(-1),
|
||||
ToChatType: proto.Int32(-1),
|
||||
BoolSupportProgressive: proto.Bool(true),
|
||||
FileWidth: proto.Int32(1280),
|
||||
FileHeight: proto.Int32(720),
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
41
utils/sys.go
41
utils/sys.go
@ -1,10 +1,18 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"errors"
|
||||
"io"
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type multiReadSeeker struct {
|
||||
readers []io.ReadSeeker
|
||||
multiReader io.Reader
|
||||
}
|
||||
|
||||
func IsChanClosed(ch interface{}) bool {
|
||||
if reflect.TypeOf(ch).Kind() != reflect.Chan {
|
||||
panic("object is not a channel.")
|
||||
@ -15,3 +23,36 @@ func IsChanClosed(ch interface{}) bool {
|
||||
ptr += unsafe.Sizeof(uint16(0))
|
||||
return *(*uint32)(unsafe.Pointer(ptr)) > 0
|
||||
}
|
||||
|
||||
func GetMd5AndLength(r io.Reader) ([]byte, int64) {
|
||||
h := md5.New()
|
||||
length, _ := io.Copy(h, r)
|
||||
fh := h.Sum(nil)
|
||||
return fh[:], length
|
||||
}
|
||||
|
||||
func (r *multiReadSeeker) Read(p []byte) (int, error) {
|
||||
if r.multiReader == nil {
|
||||
var readers []io.Reader
|
||||
for i := range r.readers {
|
||||
_, _ = r.readers[i].Seek(0, io.SeekStart)
|
||||
readers = append(readers, r.readers[i])
|
||||
}
|
||||
r.multiReader = io.MultiReader(readers...)
|
||||
}
|
||||
return r.multiReader.Read(p)
|
||||
}
|
||||
|
||||
func (r *multiReadSeeker) Seek(offset int64, whence int) (int64, error) {
|
||||
if whence != 0 || offset != 0 {
|
||||
return -1, errors.New("unsupported offset")
|
||||
}
|
||||
r.multiReader = nil
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func MultiReadSeeker(r ...io.ReadSeeker) io.ReadSeeker {
|
||||
return &multiReadSeeker{
|
||||
readers: r,
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user