package services import ( "bytes" "encoding/base64" "encoding/binary" "encoding/json" "errors" "eta/eta_api/models" "eta/eta_api/services/alarm_msg" "eta/eta_api/utils" "fmt" "github.com/PuerkitoBio/goquery" "github.com/kgiannakakis/mp3duration/src/mp3duration" "html" "io" "io/ioutil" "os" "strings" "time" "unicode" ) func CreateVideo(report *models.Report) (err error) { defer func() { if err != nil { utils.FileLog.Error("CreateVideo Err:%s", err.Error()) go alarm_msg.SendAlarmMsg("CreateVideo, Err:"+err.Error(), 3) //go utils.SendEmail(utils.APPNAME+"【"+utils.RunMode+"】"+"失败提醒", "Err:"+err.Error(), utils.EmailSendToUsers) } }() // 获取基础配置, 若未配置则直接返回 conf, e := models.GetBusinessConf() if e != nil { err = fmt.Errorf("获取基础配置失败, Err: " + e.Error()) return } if conf[models.BusinessConfUseXf] != "true" { return } if conf[models.BusinessConfXfAppid] == "" || conf[models.BusinessConfXfApiKey] == "" || conf[models.BusinessConfXfApiSecret] == "" || conf[models.BusinessConfXfVcn] == "" { return } var xfReq XfParams xfReq.XfAPPID = conf[models.BusinessConfXfAppid] xfReq.XfAPIKey = conf[models.BusinessConfXfApiKey] xfReq.XfAPISecret = conf[models.BusinessConfXfApiSecret] ct, err := time.Parse(utils.FormatDateTime, report.CreateTime) createTime := ct.Format("0102") videoName := report.Title + "(" + createTime + ")" content := html.UnescapeString(report.Content) content = strings.Replace(content, "Powered", "", -1) content = strings.Replace(content, "by", "", -1) content = strings.Replace(content, "Froala", "", -1) content = strings.Replace(content, "Editor", "", -1) doc, err := goquery.NewDocumentFromReader(strings.NewReader(content)) if err != nil { return } param := new(models.XfSendParam) param.Common.AppId = conf[models.BusinessConfXfAppid] param.Business.Aue = "lame" param.Business.Sfl = 1 param.Business.Auf = "audio/L16;rate=16000" param.Business.Vcn = conf[models.BusinessConfXfVcn] param.Business.Speed = 50 param.Business.Volume = 100 param.Business.Pitch = 50 param.Business.Bgs = 0 param.Business.Tte = "UTF8" param.Business.Reg = "2" param.Business.Rdn = "0" param.Data.Status = 2 videoContent := doc.Text() saveName := utils.GetRandStringNoSpecialChar(16) + ".mp3" savePath := "./" + saveName defer func() { if utils.FileIsExist(savePath) { os.Remove(savePath) } }() // 如果没有文本内容,那么就不生成了 videoContent = strings.TrimSpace(videoContent) if videoContent == `` { return } contentArr := GetChineseCount(videoContent) for _, v := range contentArr { newText := v param.Data.Text = base64.StdEncoding.EncodeToString([]byte(newText)) result, err := json.Marshal(param) if err != nil { return err } err = GetXfVideo(result, savePath, xfReq) if err != nil { err = errors.New("GetXfVideo Err:" + err.Error()) utils.FileLog.Error("GetXfVideo err", err.Error()) return err } time.Sleep(5 * time.Second) } uploadUrl := `` ossClient := NewOssClient() if ossClient == nil { err = fmt.Errorf("初始化OSS服务失败") return } uploadUrl, err = ossClient.UploadFile(saveName, savePath, "") if err != nil { err = fmt.Errorf("文件上传失败, Err: %s", err.Error()) return } fileBody, err := os.ReadFile(savePath) videoSize := len(fileBody) sizeFloat := (float64(videoSize) / float64(1024)) / float64(1024) sizeStr := utils.SubFloatToFloatStr(sizeFloat, 2) playSeconds, err := mp3duration.Calculate(savePath) if playSeconds <= 0 { playSeconds, err = utils.GetVideoPlaySeconds(savePath) if err != nil { err = errors.New("GetVideoPlaySeconds Err:" + err.Error()) return } } // 修改报告的音频信息 err = models.ModifyReportVideo(report.Id, uploadUrl, videoName, sizeStr, playSeconds) return } func GetChineseCount(str1 string) []string { fontArr := make([]string, 0) str := "" count := 0 for _, char := range str1 { str += string(char) if unicode.Is(unicode.Han, char) { count++ if count >= 1700 { fontArr = append(fontArr, str) str = "" count = 0 } } } fontArr = append(fontArr, str) return fontArr } // BoxHeader 信息头 type BoxHeader struct { Size uint32 FourccType [4]byte Size64 uint64 } // GetMP4Duration 获取视频时长,以秒计 func GetMP4Duration(reader io.ReaderAt) (lengthOfTime uint32, err error) { var info = make([]byte, 0x10) var boxHeader BoxHeader var offset int64 = 0 // 获取moov结构偏移 for { _, err = reader.ReadAt(info, offset) if err != nil { return } boxHeader = getHeaderBoxInfo(info) fourccType := getFourccType(boxHeader) if fourccType == "moov" { break } // 有一部分mp4 mdat尺寸过大需要特殊处理 if fourccType == "mdat" { if boxHeader.Size == 1 { offset += int64(boxHeader.Size64) continue } } offset += int64(boxHeader.Size) } // 获取moov结构开头一部分 moovStartBytes := make([]byte, 0x100) _, err = reader.ReadAt(moovStartBytes, offset) if err != nil { return } // 定义timeScale与Duration偏移 timeScaleOffset := 0x1C durationOffest := 0x20 timeScale := binary.BigEndian.Uint32(moovStartBytes[timeScaleOffset : timeScaleOffset+4]) Duration := binary.BigEndian.Uint32(moovStartBytes[durationOffest : durationOffest+4]) lengthOfTime = Duration / timeScale return } // getHeaderBoxInfo 获取头信息 func getHeaderBoxInfo(data []byte) (boxHeader BoxHeader) { buf := bytes.NewBuffer(data) binary.Read(buf, binary.BigEndian, &boxHeader) return } // getFourccType 获取信息头类型 func getFourccType(boxHeader BoxHeader) (fourccType string) { fourccType = string(boxHeader.FourccType[:]) return } // CreateReportVideo 生成报告video func CreateReportVideo(reportTitle, reportContent, reportTime string) (uploadUrl, videoName, sizeStr string, playSeconds float64, err error) { defer func() { if err != nil { utils.FileLog.Error("CreateReportVideo Err:%s", err.Error()) go alarm_msg.SendAlarmMsg("CreateReportVideo, reportTitle:"+reportTitle+", Err:"+err.Error(), 3) //go utils.SendEmail(utils.APPNAME+"【"+utils.RunMode+"】"+"失败提醒", "CreateReportVideo, reportTitle:" + reportTitle +", Err:"+err.Error(), utils.EmailSendToUsers) } }() // 如果没有文本内容,那么就不生成了 reportContent = strings.TrimSpace(reportContent) if reportContent == `` { return } // 获取基础配置, 若未配置则直接返回 conf, e := models.GetBusinessConf() if e != nil { err = fmt.Errorf("获取基础配置失败, Err: " + e.Error()) return } if conf[models.BusinessConfUseXf] != "true" { return } if conf[models.BusinessConfXfAppid] == "" || conf[models.BusinessConfXfApiKey] == "" || conf[models.BusinessConfXfApiSecret] == "" || conf[models.BusinessConfXfVcn] == "" { return } var xfReq XfParams xfReq.XfAPPID = conf[models.BusinessConfXfAppid] xfReq.XfAPIKey = conf[models.BusinessConfXfApiKey] xfReq.XfAPISecret = conf[models.BusinessConfXfApiSecret] ct, err := time.Parse(utils.FormatDateTime, reportTime) if err != nil { return } createTime := ct.Format("0102") videoName = reportTitle + "(" + createTime + ")" content := html.UnescapeString(reportContent) content = strings.Replace(content, "Powered", "", -1) content = strings.Replace(content, "by", "", -1) content = strings.Replace(content, "Froala", "", -1) content = strings.Replace(content, "Editor", "", -1) doc, err := goquery.NewDocumentFromReader(strings.NewReader(content)) if err != nil { return } param := new(models.XfSendParam) param.Common.AppId = conf[models.BusinessConfXfAppid] param.Business.Aue = "lame" param.Business.Sfl = 1 param.Business.Auf = "audio/L16;rate=16000" param.Business.Vcn = conf[models.BusinessConfXfVcn] param.Business.Speed = 50 param.Business.Volume = 100 param.Business.Pitch = 50 param.Business.Bgs = 0 param.Business.Tte = "UTF8" param.Business.Reg = "2" param.Business.Rdn = "0" param.Data.Status = 2 videoContent := doc.Text() // 如果没有文本内容,那么就不生成了 videoContent = strings.TrimSpace(videoContent) if videoContent == `` { return } saveName := utils.GetRandStringNoSpecialChar(16) + ".mp3" savePath := "./" + saveName defer func() { if utils.FileIsExist(savePath) { os.Remove(savePath) } }() contentArr := GetChineseCount(videoContent) for _, v := range contentArr { newText := v param.Data.Text = base64.StdEncoding.EncodeToString([]byte(newText)) result, tmpErr := json.Marshal(param) if tmpErr != nil { return } err = GetXfVideo(result, savePath, xfReq) if err != nil { err = errors.New("GetXfVideo Err:" + err.Error()) utils.FileLog.Error("GetXfVideo err", err.Error()) return } time.Sleep(5 * time.Second) } fmt.Println("savePath:" + savePath) //上传到阿里云 和 minio //if utils.ObjectStorageClient == "minio" { // uploadUrl, err = UploadAudioToMinIo(saveName, savePath) // if err != nil { // err = errors.New("UploadAudioAliyun Err:" + err.Error()) // return // } //} else { // uploadUrl, err = UploadAudioAliyun(saveName, savePath) // if err != nil { // err = errors.New("UploadAudioAliyun Err:" + err.Error()) // return // } //} ossClient := NewOssClient() if ossClient == nil { err = fmt.Errorf("初始化OSS服务失败") return } uploadUrl, err = ossClient.UploadFile(saveName, savePath, "") if err != nil { err = fmt.Errorf("文件上传失败, Err: %s", err.Error()) return } fileBody, err := ioutil.ReadFile(savePath) videoSize := len(fileBody) sizeFloat := (float64(videoSize) / float64(1024)) / float64(1024) sizeStr = utils.SubFloatToFloatStr(sizeFloat, 2) playSeconds, err = mp3duration.Calculate(savePath) if playSeconds <= 0 { playSeconds, err = utils.GetVideoPlaySeconds(savePath) if err != nil { err = errors.New("GetVideoPlaySeconds Err:" + err.Error()) return } } return }