HLS-builder/upload.go
2024-05-09 16:11:13 +08:00

268 lines
8.8 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* @Author: BlackTeay
* @Date: 2024-05-09 15:47:52
* @LastEditTime: 2024-05-09 16:07:41
* @LastEditors: BlackTeay
* @Description:
* @FilePath: /hls_builder/upload.go
* Copyright 2024 JLNTV NMTD, All Rights Reserved.
*/
package main
import (
"fmt"
"log"
"os"
"os/exec"
"path/filepath"
"regexp"
"strconv"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/widget"
)
type UploadStats struct {
Successed int
Failed int
TotalSize string
AverageSpeed string
Elapsed string
}
// 上传到Ucloud
func upload(localPath string, keyPath string, progressBar *widget.ProgressBar, myWindow fyne.Window, onComplete func()) {
fmt.Println("上传到Ucloud")
progressBar.SetValue(0)
fmt.Println("localPath:", localPath)
fmt.Println("keyPath:", keyPath)
// 统计 .ts .m3u8 文件数量
tsFiles, m3u8Files, err := countMediaFiles(localPath)
if err != nil {
log.Fatalf("Error counting media files: %s", err)
}
fmt.Printf("Total .ts files: %d\n", tsFiles)
fmt.Printf("Total .m3u8 files: %d\n", m3u8Files)
filesTotal := float64(tsFiles + m3u8Files)
fmt.Printf("Total files: %d\n", int(filesTotal))
// 获取 us3cli 的路径
us3cliPath, cleanup, err := getUs3cliPath()
if err != nil {
log.Fatal(err)
}
defer cleanup() // 确保在函数返回时删除临时目录
bucket := "us3://jlntv-live/replay/" + keyPath
// us3ConfigPath, cleanup, err := getUs3Config()
// if err != nil {
// log.Fatal(err)
// }
// defer cleanup() // 确保在函数返回时删除临时目录
// cmd := exec.Command(us3cliPath, "cp", localPath, bucket, "-r", "--parallel", "20", "--config", us3ConfigPath)
cmd := exec.Command(us3cliPath, "cp", localPath, bucket, "-r", "--parallel", "20", "--accesskey", "TOKEN_6096c736-12b7-4c20-bfa5-e85e0e9c9b65", "--secretkey", "cc8a5965-3325-4231-9f64-a6626f624049", "--endpoint", "cn-bj.ufileos.com")
//输出cmd
fmt.Println(cmd.String())
stdout, err := cmd.StdoutPipe()
if err != nil {
log.Fatal("Error creating stdout pipe:", err)
}
if err := cmd.Start(); err != nil {
log.Fatalf("Failed to start command: %s", err)
}
progress := 0.0
go func() {
buffer := make([]byte, 4096) // 创建一个足够大的buffer
for {
n, err := stdout.Read(buffer)
if n > 0 {
fmt.Print("STDOUT:", string(buffer[:n])) // 打印实时输出
filesUploadedTotal, err := extractTotalValue(string(buffer[:n]))
if err != nil {
fmt.Println("Error:", err)
} else {
fmt.Println("Total:", filesUploadedTotal)
}
stats, err := parseUploadStats(string(buffer[:n]))
if err != nil {
fmt.Println("Error parsing upload stats:", err)
} else {
fmt.Printf("Uploaded: %d, Failed: %d, Total Size: %s, Speed: %s\n",
stats.Successed, stats.Failed, stats.TotalSize, stats.AverageSpeed)
if float64(stats.Successed) == filesTotal {
progressBar.SetValue(1)
showCustomDialog := func() {
replayURL := fmt.Sprintf("https://video.jlntv.cn/replay/%s/playlist.m3u8", keyPath)
copyBtn := widget.NewButton("复制地址", func() {
fmt.Println("copyed:", replayURL)
// dialog.ShowInformation("Submitted", "You submitted: "+input.Text, myWindow)
clipboard := myWindow.Clipboard()
clipboard.SetContent(replayURL)
// showToast(myWindow, "地址已复制到剪贴板", 3*time.Second)
dialog.ShowInformation("提示", "地址已复制到剪贴板!", myWindow)
})
// 创建自定义内容的容器
content := container.NewVBox(
widget.NewLabel("Upload Stats:"),
widget.NewLabel(fmt.Sprintf("用时:%s秒", stats.Elapsed)),
widget.NewLabel(fmt.Sprintf("成功:%d", stats.Successed)),
widget.NewLabel(fmt.Sprintf("失败:%d", stats.Failed)),
widget.NewLabel(fmt.Sprintf("平均速度:%s", stats.AverageSpeed)),
widget.NewLabel(fmt.Sprintf("总大小:%s", stats.TotalSize)),
widget.NewLabel(fmt.Sprintf("回看地址: %s", replayURL)),
copyBtn,
)
// 设置内容容器的最小尺寸
content.Resize(fyne.NewSize(500, 600)) // 你可以根据需要调整这个尺寸
// 创建并显示自定义对话框
customDialog := dialog.NewCustom("上传成功", "关闭", content, myWindow)
customDialog.Show()
}
showCustomDialog()
// dialog.ShowInformation("完成", fmt.Sprintf("✅ HLS上传完毕\n总用时:%s秒\n成功:%d 失败:%d 总大小:%s 平均速度:%s", stats.Elapsed, stats.Successed, stats.Failed, stats.TotalSize, stats.AverageSpeed), myWindow)
fmt.Println("Upload completed!")
} else {
progress = filesUploadedTotal / filesTotal
fmt.Println("Progress:", progress)
progressBar.SetValue(progress)
}
}
}
if err != nil {
break
}
}
}()
if err := cmd.Wait(); err != nil {
log.Printf("Command finished with error: %v", err)
}
if onComplete != nil {
onComplete()
}
}
// parseUploadStats 函数解析上传统计信息字符串,并返回一个包含上传统计详情的结构体指针。
// 参数 line 为待解析的上传统计信息字符串。
// 返回 *UploadStats 指向解析出的上传统计详情结构体。
// 返回 error 表示在解析过程中遇到的任何错误。
func parseUploadStats(line string) (*UploadStats, error) {
stats := &UploadStats{}
var err error
// 初始化正则表达式用于匹配不同的上传统计信息。
successedRegex := regexp.MustCompile(`(\d+) Successed`)
failedRegex := regexp.MustCompile(`(\d+) Failed`)
sizeRegex := regexp.MustCompile(`Size: ([\d.]+ MB)`)
speedRegex := regexp.MustCompile(`Average speed ([\d.]+ MB/s)`)
elapsedRegex := regexp.MustCompile(`Elapsed\s*:\s*([\d.]+)s`)
// 提取成功上传的文件数量。
successedMatches := successedRegex.FindStringSubmatch(line)
if len(successedMatches) > 1 {
stats.Successed, err = strconv.Atoi(successedMatches[1])
if err != nil {
return nil, fmt.Errorf("error parsing successed number: %v", err)
}
}
// 提取失败上传的文件数量。
failedMatches := failedRegex.FindStringSubmatch(line)
if len(failedMatches) > 1 {
stats.Failed, err = strconv.Atoi(failedMatches[1])
if err != nil {
return nil, fmt.Errorf("error parsing failed number: %v", err)
}
}
// 提取上传的总大小。
sizeMatches := sizeRegex.FindStringSubmatch(line)
if len(sizeMatches) > 1 {
stats.TotalSize = sizeMatches[1]
}
// 提取平均上传速度。
speedMatches := speedRegex.FindStringSubmatch(line)
if len(speedMatches) > 1 {
stats.AverageSpeed = speedMatches[1]
}
// 提取上传所花费的时间。
elapsedMatches := elapsedRegex.FindStringSubmatch(line)
if len(elapsedMatches) > 1 {
stats.Elapsed = elapsedMatches[1]
}
return stats, nil
}
// extractTotalValue 函数从给定的文本中提取总值。
//
// 参数:
//
// text string - 包含待搜索总值的文本。
//
// 返回值:
//
// float64 - 从文本中提取到的总值如果未找到则返回默认值200。
// error - 如果在提取过程中发生错误则返回相应的错误否则返回nil。
func extractTotalValue(text string) (float64, error) {
// 使用正则表达式匹配文本中的总值部分
re := regexp.MustCompile(`Total:(\d+)`)
matches := re.FindStringSubmatch(text)
if len(matches) > 1 {
// 将匹配到的总值字符串转换为整数
total, err := strconv.Atoi(matches[1])
if err != nil {
// 如果转换过程中发生错误,返回错误信息
return 0, err
}
// 返回转换后的总值
return float64(total), nil
}
// 如果未在文本中找到总值返回默认值200和一个错误信息
return 200, fmt.Errorf("no total found in the text")
}
// countMediaFiles 统计给定目录中 .ts 和 .m3u8 文件的数量。
//
// 参数:
//
// directory string - 需要遍历统计文件的目录路径。
//
// 返回值:
//
// int - .ts 文件的数量。
// int - .m3u8 文件的数量。
// error - 遍历目录过程中遇到的任何错误。
func countMediaFiles(directory string) (int, int, error) {
var tsCount, m3u8Count int // 分别用于记录 .ts 和 .m3u8 文件的数量
// 使用 filepath.Walk 遍历指定目录及其子目录中的所有文件
err := filepath.Walk(directory, func(path string, info os.FileInfo, err error) error {
// 检查遍历过程是否遇到错误
if err != nil {
return err // 如果有错误,则终止遍历并返回该错误
}
// 检查当前路径项是否为文件,不遍历目录
if !info.IsDir() {
// 根据文件扩展名统计 .ts 和 .m3u8 文件数量
switch filepath.Ext(info.Name()) {
case ".ts":
tsCount++ // .ts 文件计数
case ".m3u8":
m3u8Count++ // .m3u8 文件计数
}
}
return nil // 继续遍历下一个文件或目录
})
// 返回 .ts 和 .m3u8 文件的计数结果,以及遍历过程中可能遇到的错误
return tsCount, m3u8Count, err
}