package utils import ( "encoding/base64" "fmt" "io" "log" "os" "os/exec" "path/filepath" "pbx-api-gin/internal/pkg/configs" "pbx-api-gin/pkg/lfshook" "strconv" "strings" ) // FileExists checks if a file exists and is not a directory before we // try using it to prevent further errors. func FileExists(filename string) bool { info, err := os.Stat(filename) if os.IsNotExist(err) { return false } return !info.IsDir() } func GetDuration(filePath string) (int, error) { cmd := exec.Command("ffprobe", "-v", "quiet", "-show_entries", "format=duration", "-of", "default=nw=1:nokey=1", filePath, ) output, err := cmd.Output() if err != nil { return 0, err } // 解析输出(纯数字,单位秒) durationStr := strings.TrimSpace(string(output)) duration, err := strconv.ParseFloat(durationStr, 64) if err != nil { return 0, err } return int(duration), nil } func AudioFileEncode(dstFile, srcFile string) error { // Step 1:获取 Base64 编码结果 data := "sripis123" encoded := base64.StdEncoding.EncodeToString([]byte(data)) // Step 2: 创建一个新文件,写入 Base64 编码结果 outputFile, err := os.Create(dstFile) if err != nil { return err } defer outputFile.Close() _, err = outputFile.WriteString(encoded) if err != nil { return err } // Step 3: 打开 audio.wav 文件 audioFile, err := os.Open(srcFile) if err != nil { return err } defer audioFile.Close() // Step 4: 读取源文件的全部内容 data1, err := io.ReadAll(audioFile) if err != nil { return err } // Step 5: 对读取的内容进行 Base64 编码 encoded1 := base64.StdEncoding.EncodeToString(data1) // Step 6: 写入 Base64 编码后的内容 _, err = outputFile.WriteString(encoded1) if err != nil { return err } return err } // ConvertAndSegmentWAV 将 WAV 文件转为 22kHz 并每 180 秒切分,返回生成的文件名列表 func ConvertAndSegmentWAV(inputFile string, outputPrefix string) ([]string, error) { // 输出文件命名模式,如:output_%03d.wav outputPattern := outputPrefix + "_%03d.wav" // 构建 ffmpeg 命令 cmd := exec.Command("ffmpeg", "-i", inputFile, // 输入文件 "-ar", "22050", // 设置采样率为 22050 Hz (22kHz) "-f", "segment", // 启用分段模式 "-segment_time", "180", // 每段 180 秒 "-c:a", "pcm_s16le", // 音频编码格式(标准 WAV) "-reset_timestamps", "1", // 重置时间戳,每个片段从 0 开始 outputPattern, // 输出文件命名规则 ) // 执行命令并捕获输出 output, err := cmd.CombinedOutput() if err != nil && !strings.Contains(string(output), "Conversion failed!") { // 即使有警告(如非关键错误),只要文件生成了就继续解析 log.Printf("ffmpeg 执行时有警告: %v", err) } // 解析输出,提取所有生成的文件名 files := extractFilenamesFromFFmpegOutput(outputPrefix) lfshook.NewLogger().Infof("=============== found after convert outfiles %+v ", files) return files, nil } // extractFilenamesFromFFmpegOutput 从 ffmpeg 输出日志中提取生成的文件名 func extractFilenamesFromFFmpegOutput(prefix string) []string { pattern := fmt.Sprintf("%s_*", prefix) matches, err := filepath.Glob(pattern) if err != nil { log.Fatal("模式匹配错误:", err) } return matches } func WriteLogToFile(message string) { // 打开文件,支持追加模式,如果文件不存在则创建 file, err := os.OpenFile(configs.ConfigGlobal.RecordEventLog, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) if err != nil { return } defer file.Close() // 创建一个写入到文件的日志器 logger := log.New(file, "", log.LstdFlags) // 写入日志消息 logger.Println(message) } var Logger *log.Logger func init() { file, _ := os.OpenFile("/data/test/log/recordEvent.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) Logger = log.New(file, "", log.LstdFlags) // 自动带时间戳 } func GetPadInfo(base string) (padType, padNum, connectedCab string) { // 按 '/' 分割字符串 parts := strings.Split(base, "/") // 取最后一段 lastPart := parts[len(parts)-1] // 最后一段中,按‘-’分割返回前三段:parts[0], parts[1], parts[2] → PAD-2411-1411 ret := strings.Split(lastPart, "-") if len(ret) < 3 { return } return ret[1], ret[2], ret[3] }