mirror of
https://github.com/yincongcyincong/wechat_chatter.git
synced 2026-07-15 10:26:52 +08:00
receive video
This commit is contained in:
+34
-7
@@ -7,6 +7,7 @@ var buf2RespAddr = baseAddr.add(0x3721FA0)
|
||||
var downloadImagAddr = baseAddr.add(0x4A6975C)
|
||||
var startDownloadMedia = baseAddr.add(0x494663C)
|
||||
var downloadFileAddr = baseAddr.add(0x4A084EC)
|
||||
var downloadVideoAddr = baseAddr.add(0x4A2A268)
|
||||
|
||||
var downloadGlobalX0;
|
||||
var downloadFileX1 = ptr(0)
|
||||
@@ -144,14 +145,15 @@ function setReceiver() {
|
||||
downloadGlobalX0 = this.context.x0;
|
||||
var fileIDAddr = this.context.x1.add(0x40).readPointer();
|
||||
var fileId = fileIDAddr?.readUtf8String();
|
||||
if (!fileId.endsWith("_1")) {
|
||||
return
|
||||
}
|
||||
|
||||
console.log(" [+] download file: ", fileId);
|
||||
const t = this.context.x1.add(0xA0).readU32()
|
||||
console.log(" [+] download file: ", fileId, " type", t);
|
||||
if (t === 3) {
|
||||
this.context.x1.add(0xA0).writeU32(0x02);
|
||||
if (fileId.endsWith("_1")) {
|
||||
this.context.x1.add(0xA0).writeU32(0x02);
|
||||
}
|
||||
if (fileId.endsWith("_31")) {
|
||||
this.context.x1.add(0xA0).writeU32(0x04);
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -197,6 +199,27 @@ function setReceiver() {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Interceptor.attach(downloadVideoAddr, {
|
||||
onEnter: function (args) {
|
||||
var dataPtr = this.context.x1;
|
||||
var dataLen = this.context.x2.toInt32();
|
||||
var fileId = this.context.x22.add(0x40).readPointer().readUtf8String();
|
||||
var cdnUrl = this.context.x22.add(0x58).readPointer().readUtf8String();
|
||||
|
||||
if (dataLen > 0) {
|
||||
var buffer = dataPtr.readByteArray(dataLen);
|
||||
var uint8Array = new Uint8Array(buffer);
|
||||
|
||||
send({
|
||||
type: "download",
|
||||
media: Array.from(uint8Array),
|
||||
file_id: fileId,
|
||||
cdn_url: cdnUrl,
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -228,8 +251,10 @@ function getMessages(content, sender, mediaContent) {
|
||||
break
|
||||
}
|
||||
}
|
||||
} else if (content.startsWith("<msg><emoji")) {
|
||||
} else if (part.startsWith("<msg><emoji")) {
|
||||
messages.push({type: "face", data: {text: part}});
|
||||
} else if (part.startsWith("<?xml version=\"1.0\"?><msg><videomsg")) {
|
||||
messages.push({type: "video", data: {text: part}});
|
||||
} else {
|
||||
messages.push({type: "text", data: {text: part}});
|
||||
}
|
||||
@@ -258,6 +283,8 @@ function getMessages(content, sender, mediaContent) {
|
||||
}
|
||||
} else if (content.startsWith("<msg><emoji")) {
|
||||
messages.push({type: "face", data: {text: content}});
|
||||
} else if (content.startsWith("<?xml version=\"1.0\"?><msg><videomsg")) {
|
||||
messages.push({type: "video", data: {text: content}});
|
||||
} else {
|
||||
messages.push({type: "text", data: {text: content}});
|
||||
}
|
||||
|
||||
+6
-1
@@ -19,7 +19,12 @@ func Download(rawMsg []byte) error {
|
||||
if beforeDownloadReq.FilePath != "" {
|
||||
return nil
|
||||
}
|
||||
beforeDownloadReq.Media = append(beforeDownloadReq.Media, downloadReq.Media...)
|
||||
if time.Now().UnixMilli()-beforeDownloadReq.LastAppendTime > 10000000 {
|
||||
beforeDownloadReq.Media = downloadReq.Media
|
||||
} else {
|
||||
beforeDownloadReq.Media = append(beforeDownloadReq.Media, downloadReq.Media...)
|
||||
}
|
||||
|
||||
beforeDownloadReq.LastAppendTime = time.Now().UnixMilli()
|
||||
} else {
|
||||
downloadReq.LastAppendTime = time.Now().UnixMilli()
|
||||
|
||||
@@ -118,6 +118,7 @@ type FileMsg struct {
|
||||
XMLName xml.Name `xml:"msg"`
|
||||
Image Image `xml:"img"`
|
||||
Emoji Emoji `xml:"emoji"`
|
||||
Video VideoMsg `xml:"videomsg"`
|
||||
GameExt GameExt `xml:"gameext"`
|
||||
AppMsg AppMsg `xml:"appmsg"`
|
||||
ExtCommonInfo ExtCommonInfo `xml:"extcommoninfo"`
|
||||
@@ -240,3 +241,26 @@ type AppInfo struct {
|
||||
Version string `xml:"version"`
|
||||
AppName string `xml:"appname"`
|
||||
}
|
||||
|
||||
type VideoMsg struct {
|
||||
AesKey string `xml:"aeskey,attr"` // 视频解密 Key
|
||||
CdnVideoUrl string `xml:"cdnvideourl,attr"` // 视频 CDN 地址
|
||||
CdnThumbAesKey string `xml:"cdnthumbaeskey,attr"` // 缩略图解密 Key
|
||||
CdnThumbUrl string `xml:"cdnthumburl,attr"` // 缩略图 CDN 地址
|
||||
Length int64 `xml:"length,attr"` // 视频文件大小 (字节)
|
||||
PlayLength int `xml:"playlength,attr"` // 播放时长 (秒)
|
||||
CdnThumbLength int `xml:"cdnthumblength,attr"` // 缩略图大小
|
||||
CdnThumbWidth int `xml:"cdnthumbwidth,attr"` // 缩略图宽度
|
||||
CdnThumbHeight int `xml:"cdnthumbheight,attr"` // 缩略图高度
|
||||
FromUsername string `xml:"fromusername,attr"` // 发送者 ID
|
||||
Md5 string `xml:"md5,attr"` // 视频 MD5
|
||||
NewMd5 string `xml:"newmd5,attr"` // 新版 MD5
|
||||
IsPlaceholder int `xml:"isplaceholder,attr"` // 是否占位符
|
||||
RawMd5 string `xml:"rawmd5,attr"` // 原始 MD5
|
||||
RawLength int64 `xml:"rawlength,attr"` // 原始长度
|
||||
CdnRawVideoUrl string `xml:"cdnrawvideourl,attr"` // 原始视频 CDN 地址
|
||||
CdnRawVideoAesKey string `xml:"cdnrawvideoaeskey,attr"` // 原始视频解密 Key
|
||||
OverwriteNewMsgId int64 `xml:"overwritenewmsgid,attr"` // 覆盖消息 ID
|
||||
OriginSourceMd5 string `xml:"originsourcemd5,attr"` // 原始源 MD5
|
||||
IsAd int `xml:"isad,attr"` // 是否为广告
|
||||
}
|
||||
|
||||
+34
-7
@@ -301,6 +301,7 @@ var uploadOnCompleteFuncAddr = baseAddr.add({{.uploadOnCompleteFuncAddr}});
|
||||
var downloadImagAddr = baseAddr.add({{.downloadImagAddr}});
|
||||
var startDownloadMedia = baseAddr.add({{.startDownloadMedia}})
|
||||
var downloadFileAddr = baseAddr.add({{.downloadFileAddr}})
|
||||
var downloadVideoAddr = baseAddr.add({{.downloadVideoAddr}})
|
||||
|
||||
var downloadGlobalX0;
|
||||
var downloadFileX1 = ptr(0)
|
||||
@@ -1313,14 +1314,15 @@ function setReceiver() {
|
||||
downloadGlobalX0 = this.context.x0;
|
||||
var fileIDAddr = this.context.x1.add(0x40).readPointer();
|
||||
var fileId = fileIDAddr?.readUtf8String();
|
||||
if (!fileId.endsWith("_1")) {
|
||||
return
|
||||
}
|
||||
|
||||
console.log(" [+] download file: ", fileId);
|
||||
const t = this.context.x1.add(0xA0).readU32()
|
||||
console.log(" [+] download file: ", fileId, " type", t);
|
||||
if (t === 3) {
|
||||
this.context.x1.add(0xA0).writeU32(0x02);
|
||||
if (fileId.endsWith("_1")) {
|
||||
this.context.x1.add(0xA0).writeU32(0x02);
|
||||
}
|
||||
if (fileId.endsWith("_31")) {
|
||||
this.context.x1.add(0xA0).writeU32(0x04);
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -1366,6 +1368,27 @@ function setReceiver() {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Interceptor.attach(downloadVideoAddr, {
|
||||
onEnter: function (args) {
|
||||
var dataPtr = this.context.x1;
|
||||
var dataLen = this.context.x2.toInt32();
|
||||
var fileId = this.context.x22.add(0x40).readPointer().readUtf8String();
|
||||
var cdnUrl = this.context.x22.add(0x58).readPointer().readUtf8String();
|
||||
|
||||
if (dataLen > 0) {
|
||||
var buffer = dataPtr.readByteArray(dataLen);
|
||||
var uint8Array = new Uint8Array(buffer);
|
||||
|
||||
send({
|
||||
type: "download",
|
||||
media: Array.from(uint8Array),
|
||||
file_id: fileId,
|
||||
cdn_url: cdnUrl,
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setImmediate(setReceiver)
|
||||
@@ -1550,8 +1573,10 @@ function getMessages(content, sender, mediaContent) {
|
||||
break
|
||||
}
|
||||
}
|
||||
} else if (content.startsWith("<msg><emoji")) {
|
||||
} else if (part.startsWith("<msg><emoji")) {
|
||||
messages.push({type: "face", data: {text: part}});
|
||||
} else if (part.startsWith("<?xml version=\"1.0\"?><msg><videomsg")) {
|
||||
messages.push({type: "video", data: {text: part}});
|
||||
} else {
|
||||
messages.push({type: "text", data: {text: part}});
|
||||
}
|
||||
@@ -1580,6 +1605,8 @@ function getMessages(content, sender, mediaContent) {
|
||||
}
|
||||
} else if (content.startsWith("<msg><emoji")) {
|
||||
messages.push({type: "face", data: {text: content}});
|
||||
} else if (content.startsWith("<?xml version=\"1.0\"?><msg><videomsg")) {
|
||||
messages.push({type: "video", data: {text: content}});
|
||||
} else {
|
||||
messages.push({type: "text", data: {text: content}});
|
||||
}
|
||||
|
||||
@@ -151,6 +151,23 @@ func DetectFileFormat(data []byte) string {
|
||||
}
|
||||
|
||||
switch {
|
||||
// 视频格式
|
||||
case bytes.HasPrefix(data, []byte{0x00, 0x00, 0x00}): // MP4/MOV 通常以 ftyp 开头,后面是具体类型
|
||||
if len(data) > 4 {
|
||||
switch string(data[4:8]) {
|
||||
case "ftyp", "moov", "mdat", "wide", "free":
|
||||
return "mp4"
|
||||
case "isom", "mp41", "mp42", "M4V ", "M4A ", "M4P ":
|
||||
return "mp4"
|
||||
}
|
||||
}
|
||||
case bytes.HasPrefix(data, []byte("FLV\x01")): // FLV
|
||||
return "flv"
|
||||
case bytes.HasPrefix(data, []byte{0x30, 0x26, 0xB2, 0x75, 0x8E, 0x66, 0xCF, 0x11}): // ASF/WMV/WMA
|
||||
if len(data) > 8 && bytes.HasPrefix(data[8:], []byte{0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}) {
|
||||
return "wmv"
|
||||
}
|
||||
|
||||
// 图片格式
|
||||
case bytes.HasPrefix(data, []byte{0xFF, 0xD8, 0xFF}):
|
||||
return "jpg"
|
||||
@@ -184,6 +201,8 @@ func DetectFileFormat(data []byte) string {
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
// detectOfficeFormat 检测 Office 2007+ 文件具体类型
|
||||
|
||||
+16
-2
@@ -130,6 +130,20 @@ func HandleMsg(jsonData []byte) ([]byte, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
msg.Data.URL = "file://" + path
|
||||
case "video":
|
||||
var fileMsg FileMsg
|
||||
err = xml.Unmarshal([]byte(msg.Data.Text), &fileMsg)
|
||||
if err != nil {
|
||||
Error("XML解析失败", "err", err)
|
||||
return nil, err
|
||||
}
|
||||
path, err := GetDownloadPath(fileMsg.Video.CdnVideoUrl, fileMsg.Video.AesKey)
|
||||
if err != nil {
|
||||
Error("获取文件路径失败", "err", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
msg.Data.URL = "file://" + path
|
||||
case "face":
|
||||
var fileMsg FileMsg
|
||||
@@ -158,7 +172,7 @@ func HandleMsg(jsonData []byte) ([]byte, error) {
|
||||
}
|
||||
|
||||
func GetDownloadPath(cdnUrl, aesKeyStr string) (string, error) {
|
||||
for i := 0; i < 5; i++ {
|
||||
for i := 0; i < 10; i++ {
|
||||
if downloadMsgInter, ok := userID2FileMsgMap.Load(cdnUrl); ok {
|
||||
downloadReq := downloadMsgInter.(*DownloadRequest)
|
||||
if downloadReq.FilePath != "" {
|
||||
@@ -170,7 +184,7 @@ func GetDownloadPath(cdnUrl, aesKeyStr string) (string, error) {
|
||||
Info("文件等待下载", "url", cdnUrl, "times", i, "last_append_time", timeSinceLastAppend)
|
||||
|
||||
// 如果数据仍在接收中(1秒内有新数据),继续等待
|
||||
if timeSinceLastAppend < 1000 && i < 4 {
|
||||
if timeSinceLastAppend < 1000 && i < 9 {
|
||||
time.Sleep(2 * time.Second)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -15,5 +15,6 @@
|
||||
"uploadOnCompleteFuncAddr": "0x36BB9C0",
|
||||
"downloadImagAddr": "0x4A6975C",
|
||||
"startDownloadMedia": "0x494663C",
|
||||
"downloadFileAddr": "0x4A084EC"
|
||||
"downloadFileAddr": "0x4A084EC",
|
||||
"downloadVideoAddr": "0x4A2A268"
|
||||
}
|
||||
Reference in New Issue
Block a user