结合us3
This commit is contained in:
parent
7dffb72bfe
commit
dc1dad08f4
@ -5,4 +5,4 @@ Website = "https://iteay.top"
|
|||||||
Name = "HLS-builder"
|
Name = "HLS-builder"
|
||||||
ID = "top.iteay.hls-builder"
|
ID = "top.iteay.hls-builder"
|
||||||
Version = "1.0.0"
|
Version = "1.0.0"
|
||||||
Build = 6
|
Build = 24
|
||||||
|
BIN
bin/.DS_Store
vendored
Normal file
BIN
bin/.DS_Store
vendored
Normal file
Binary file not shown.
BIN
bin/us3cli-mac
Executable file
BIN
bin/us3cli-mac
Executable file
Binary file not shown.
BIN
bin/us3cli-windows.exe
Executable file
BIN
bin/us3cli-windows.exe
Executable file
Binary file not shown.
6
config/userconfig.yaml
Normal file
6
config/userconfig.yaml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
accesskey: "TOKEN_f7c84db0-1671-4a70-953e-558cb25a4d74"
|
||||||
|
secretkey: "7de3463e-0ad8-4f28-ba26-b5012f78f2dc"
|
||||||
|
endpoint: "ufile.cn-bj.ucloud.cn"
|
||||||
|
encrypt: "false"
|
||||||
|
enablessl: "true"
|
||||||
|
language: "ZH"
|
10
fyne-cross.toml
Normal file
10
fyne-cross.toml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
[build]
|
||||||
|
# 目标操作系统和架构
|
||||||
|
targets = ["windows/amd64", "darwin/amd64", "linux/amd64"]
|
||||||
|
icon = "icon.png"
|
||||||
|
|
||||||
|
[package]
|
||||||
|
# 应用的唯一标识符
|
||||||
|
id = "top.iteay.hls-builder"
|
||||||
|
# 打包的资源文件目录
|
||||||
|
resources = ["bin", "font"]
|
BIN
fyne-cross/.DS_Store
vendored
BIN
fyne-cross/.DS_Store
vendored
Binary file not shown.
BIN
fyne-cross/bin/.DS_Store
vendored
BIN
fyne-cross/bin/.DS_Store
vendored
Binary file not shown.
BIN
fyne-cross/bin/darwin-amd64/hls-builder
Executable file
BIN
fyne-cross/bin/darwin-amd64/hls-builder
Executable file
Binary file not shown.
Binary file not shown.
BIN
fyne-cross/dist/.DS_Store
vendored
Normal file
BIN
fyne-cross/dist/.DS_Store
vendored
Normal file
Binary file not shown.
BIN
fyne-cross/dist/darwin-amd64/.DS_Store
vendored
Normal file
BIN
fyne-cross/dist/darwin-amd64/.DS_Store
vendored
Normal file
Binary file not shown.
BIN
fyne-cross/dist/windows-amd64/HLS-builder.exe.zip
vendored
BIN
fyne-cross/dist/windows-amd64/HLS-builder.exe.zip
vendored
Binary file not shown.
BIN
fyne-cross/tmp/darwin-amd64/Icon.png
Executable file
BIN
fyne-cross/tmp/darwin-amd64/Icon.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
5
go.mod
5
go.mod
@ -2,7 +2,10 @@ module hls-builder
|
|||||||
|
|
||||||
go 1.22.0
|
go 1.22.0
|
||||||
|
|
||||||
require fyne.io/fyne/v2 v2.4.5
|
require (
|
||||||
|
fyne.io/fyne/v2 v2.4.5
|
||||||
|
github.com/google/uuid v1.1.2
|
||||||
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
fyne.io/systray v1.10.1-0.20231115130155-104f5ef7839e // indirect
|
fyne.io/systray v1.10.1-0.20231115130155-104f5ef7839e // indirect
|
||||||
|
1
go.sum
1
go.sum
@ -160,6 +160,7 @@ github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLe
|
|||||||
github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||||
|
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
|
||||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||||
|
@ -1,34 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleName</key>
|
|
||||||
<string>hls-builder</string>
|
|
||||||
<key>CFBundleExecutable</key>
|
|
||||||
<string>hls-builder</string>
|
|
||||||
<key>CFBundleIdentifier</key>
|
|
||||||
<string>com.example.hls-builder</string>
|
|
||||||
<key>CFBundleIconFile</key>
|
|
||||||
<string>icon.icns</string>
|
|
||||||
<key>CFBundleShortVersionString</key>
|
|
||||||
<string>0.0.1</string>
|
|
||||||
<key>CFBundleSupportedPlatforms</key>
|
|
||||||
<array>
|
|
||||||
<string>MacOSX</string>
|
|
||||||
</array>
|
|
||||||
<key>CFBundleVersion</key>
|
|
||||||
<string>1</string>
|
|
||||||
<key>NSHighResolutionCapable</key>
|
|
||||||
<true/>
|
|
||||||
<key>NSSupportsAutomaticGraphicsSwitching</key>
|
|
||||||
<true/>
|
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
|
||||||
<string>6.0</string>
|
|
||||||
<key>CFBundlePackageType</key>
|
|
||||||
<string>APPL</string>
|
|
||||||
<key>LSApplicationCategoryType</key>
|
|
||||||
<string>public.app-category.</string>
|
|
||||||
<key>LSMinimumSystemVersion</key>
|
|
||||||
<string>10.11</string>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
Binary file not shown.
Binary file not shown.
149
main.go
149
main.go
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* @Author: BlackTeay
|
* @Author: BlackTeay
|
||||||
* @Date: 2024-04-30 09:37:39
|
* @Date: 2024-04-30 09:37:39
|
||||||
* @LastEditTime: 2024-05-06 09:38:30
|
* @LastEditTime: 2024-05-06 16:37:14
|
||||||
* @LastEditors: BlackTeay
|
* @LastEditors: BlackTeay
|
||||||
* @Description:
|
* @Description:
|
||||||
* @FilePath: /hls_builder/main.go
|
* @FilePath: /hls_builder/main.go
|
||||||
@ -11,13 +11,16 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"embed"
|
||||||
_ "embed"
|
_ "embed"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"image/color"
|
"image/color"
|
||||||
"log"
|
"log"
|
||||||
"math"
|
"math"
|
||||||
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -30,11 +33,15 @@ import (
|
|||||||
"fyne.io/fyne/v2/storage"
|
"fyne.io/fyne/v2/storage"
|
||||||
"fyne.io/fyne/v2/theme"
|
"fyne.io/fyne/v2/theme"
|
||||||
"fyne.io/fyne/v2/widget"
|
"fyne.io/fyne/v2/widget"
|
||||||
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:embed "font/NotoSansSC-Regular.ttf"
|
//go:embed "font/NotoSansSC-Regular.ttf"
|
||||||
var ttfBytes []byte
|
var ttfBytes []byte
|
||||||
|
|
||||||
|
//go:embed bin/*
|
||||||
|
var ffmpegFS embed.FS
|
||||||
|
|
||||||
type MyTheme struct{}
|
type MyTheme struct{}
|
||||||
|
|
||||||
func (MyTheme) Color(c fyne.ThemeColorName, v fyne.ThemeVariant) color.Color {
|
func (MyTheme) Color(c fyne.ThemeColorName, v fyne.ThemeVariant) color.Color {
|
||||||
@ -53,21 +60,62 @@ func (MyTheme) Size(s fyne.ThemeSizeName) float32 {
|
|||||||
return theme.DefaultTheme().Size(s)
|
return theme.DefaultTheme().Size(s)
|
||||||
}
|
}
|
||||||
func getFFmpegPath() string {
|
func getFFmpegPath() string {
|
||||||
var path string
|
var ffmpegFileName string
|
||||||
switch runtime.GOOS {
|
switch runtime.GOOS {
|
||||||
case "windows":
|
case "windows":
|
||||||
path = "bin/ffmpeg_windows.exe"
|
ffmpegFileName = "ffmpeg_windows.exe"
|
||||||
case "linux":
|
|
||||||
path = "bin/ffmpeg_linux"
|
|
||||||
case "darwin":
|
case "darwin":
|
||||||
path = "bin/ffmpeg_darwin"
|
ffmpegFileName = "ffmpeg_darwin"
|
||||||
default:
|
|
||||||
log.Fatalf("Unsupported operating system: %s", runtime.GOOS)
|
|
||||||
}
|
}
|
||||||
return path
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// 读取文件内容
|
||||||
|
data, err := ffmpegFS.ReadFile("bin/" + ffmpegFileName)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 写入到临时文件中
|
||||||
|
tmpDir, err := os.MkdirTemp("", "ffmpeg")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
tmpFilePath := filepath.Join(tmpDir, ffmpegFileName)
|
||||||
|
if err := os.WriteFile(tmpFilePath, data, 0755); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return tmpFilePath
|
||||||
|
}
|
||||||
|
func getUs3cliPath() string {
|
||||||
|
var us3cliFileName string
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "windows":
|
||||||
|
us3cliFileName = "us3cli-windows.exe"
|
||||||
|
case "darwin":
|
||||||
|
us3cliFileName = "us3cli-mac"
|
||||||
|
}
|
||||||
|
|
||||||
|
// 读取文件内容
|
||||||
|
data, err := ffmpegFS.ReadFile("bin/" + us3cliFileName)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 写入到临时文件中
|
||||||
|
tmpDir, err := os.MkdirTemp("", "us3cli")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
tmpFilePath := filepath.Join(tmpDir, us3cliFileName)
|
||||||
|
if err := os.WriteFile(tmpFilePath, data, 0755); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return tmpFilePath
|
||||||
|
}
|
||||||
func main() {
|
func main() {
|
||||||
|
//打印程序当前路径
|
||||||
|
log.Println("Current path:", os.Getenv("PWD"))
|
||||||
myApp := app.NewWithID("hls_builder")
|
myApp := app.NewWithID("hls_builder")
|
||||||
myApp.Settings().SetTheme(MyTheme{})
|
myApp.Settings().SetTheme(MyTheme{})
|
||||||
myWindow := myApp.NewWindow("HLS-builder 视频切片工具")
|
myWindow := myApp.NewWindow("HLS-builder 视频切片工具")
|
||||||
@ -102,6 +150,8 @@ func main() {
|
|||||||
}, myWindow)
|
}, myWindow)
|
||||||
})
|
})
|
||||||
var btnSlice *widget.Button // 预先声明btnSlice变量
|
var btnSlice *widget.Button // 预先声明btnSlice变量
|
||||||
|
var randDir string
|
||||||
|
var hlsDir string
|
||||||
btnSlice = widget.NewButton("开始切片", func() {
|
btnSlice = widget.NewButton("开始切片", func() {
|
||||||
if inputFile.Text == "没有选择视频文件" || outputDir.Text == "没有选择输出路径" {
|
if inputFile.Text == "没有选择视频文件" || outputDir.Text == "没有选择输出路径" {
|
||||||
dialog.ShowError(errors.New("请选择视频文件和输出路径"), myWindow)
|
dialog.ShowError(errors.New("请选择视频文件和输出路径"), myWindow)
|
||||||
@ -114,7 +164,16 @@ func main() {
|
|||||||
btnSlice.Disable()
|
btnSlice.Disable()
|
||||||
btnSlice.SetText("切片中...")
|
btnSlice.SetText("切片中...")
|
||||||
|
|
||||||
go sliceVideo(inputFile.Text, outputDir.Text, progressBar, func() {
|
randDir = uuid.New().String()
|
||||||
|
//在outputDir中创建一个文件夹,名为一个UUID字符串
|
||||||
|
hlsDir = outputDir.Text + "/" + randDir
|
||||||
|
//新建这个目录
|
||||||
|
err := os.Mkdir(hlsDir, 0755)
|
||||||
|
if err != nil {
|
||||||
|
dialog.ShowError(errors.New("建立随机目录出错!"), myWindow)
|
||||||
|
}
|
||||||
|
|
||||||
|
go sliceVideo(inputFile.Text, hlsDir, progressBar, func() {
|
||||||
// UI updates directly in the callback
|
// UI updates directly in the callback
|
||||||
btnSelectFile.Enable()
|
btnSelectFile.Enable()
|
||||||
btnSelectOutput.Enable()
|
btnSelectOutput.Enable()
|
||||||
@ -125,7 +184,14 @@ func main() {
|
|||||||
}, btnSelectFile, btnSelectOutput, btnSlice, myWindow)
|
}, btnSelectFile, btnSelectOutput, btnSlice, myWindow)
|
||||||
|
|
||||||
})
|
})
|
||||||
|
uploadLabel := widget.NewLabel("上传切片到Ucloud")
|
||||||
|
var btnUpload *widget.Button
|
||||||
|
btnUpload = widget.NewButton("上传到服务器", func() {
|
||||||
|
go upload(hlsDir, randDir, func() {
|
||||||
|
btnUpload.Enable()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
// btnUpload.Disable()
|
||||||
content := container.NewVBox(
|
content := container.NewVBox(
|
||||||
inputFile,
|
inputFile,
|
||||||
btnSelectFile,
|
btnSelectFile,
|
||||||
@ -133,6 +199,8 @@ func main() {
|
|||||||
btnSelectOutput,
|
btnSelectOutput,
|
||||||
progressBar,
|
progressBar,
|
||||||
btnSlice,
|
btnSlice,
|
||||||
|
uploadLabel,
|
||||||
|
btnUpload,
|
||||||
)
|
)
|
||||||
|
|
||||||
myWindow.SetContent(content)
|
myWindow.SetContent(content)
|
||||||
@ -152,7 +220,7 @@ func sliceVideo(inputFile, outputDir string, progressBar *widget.ProgressBar, on
|
|||||||
"-hls_time", "5", // 每个 HLS 段的最大时长(秒)
|
"-hls_time", "5", // 每个 HLS 段的最大时长(秒)
|
||||||
"-hls_list_size", "0", // 播放列表中的最大段数,0表示无限制
|
"-hls_list_size", "0", // 播放列表中的最大段数,0表示无限制
|
||||||
"-f", "hls", // 输出格式为 HLS
|
"-f", "hls", // 输出格式为 HLS
|
||||||
"-hls_segment_filename", outputDir+"/%04d.ts", // 段文件的命名方式
|
"-hls_segment_filename", outputDir+"/%08d.ts", // 段文件的命名方式
|
||||||
outputDir+"/playlist.m3u8", // 输出的 m3u8 文件位置
|
outputDir+"/playlist.m3u8", // 输出的 m3u8 文件位置
|
||||||
)
|
)
|
||||||
// cmd.Stderr = os.Stderr // 将 stderr 直接定向到系统的 stderr,以便直接调试 FFmpeg 的输出
|
// cmd.Stderr = os.Stderr // 将 stderr 直接定向到系统的 stderr,以便直接调试 FFmpeg 的输出
|
||||||
@ -191,10 +259,10 @@ func sliceVideo(inputFile, outputDir string, progressBar *widget.ProgressBar, on
|
|||||||
progressBar.SetValue(1.0) // 设置进度条到最大值
|
progressBar.SetValue(1.0) // 设置进度条到最大值
|
||||||
fyne.CurrentApp().SendNotification(&fyne.Notification{
|
fyne.CurrentApp().SendNotification(&fyne.Notification{
|
||||||
Title: "HLS-builder 视频切片工具",
|
Title: "HLS-builder 视频切片工具",
|
||||||
Content: fmt.Sprintf("✅ 视频切片完成! 总用时: %s", formattedDuration),
|
Content: fmt.Sprintf("✅ 视频切片完成! 总用时: %s\n存储位置:%s", formattedDuration, outputDir),
|
||||||
})
|
})
|
||||||
|
|
||||||
dialog.ShowInformation("完成", fmt.Sprintf("✅ 视频切片完成! 总用时: %s", formattedDuration), myWindow)
|
dialog.ShowInformation("完成", fmt.Sprintf("✅ 视频切片完成! 总用时: %s\n存储位置:%s", formattedDuration, outputDir), myWindow)
|
||||||
|
|
||||||
log.Println("视频切片完成")
|
log.Println("视频切片完成")
|
||||||
break
|
break
|
||||||
@ -292,3 +360,54 @@ func formatDuration(d time.Duration) string {
|
|||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 上传到Ucloud
|
||||||
|
func upload(localPath string, keyPath string, onComplete func()) {
|
||||||
|
fmt.Println("上传到Ucloud")
|
||||||
|
fmt.Println("localPath:", localPath)
|
||||||
|
fmt.Println("keyPath:", keyPath)
|
||||||
|
|
||||||
|
us3cliPath := getUs3cliPath()
|
||||||
|
bucket := "us3://jlntv-live/replay/" + keyPath
|
||||||
|
|
||||||
|
cmd := exec.Command(us3cliPath, "cp", localPath, bucket, "-r", "--parallel", "10", "--reduce")
|
||||||
|
|
||||||
|
// 获取命令的标准输出和错误输出
|
||||||
|
stdout, err := cmd.StdoutPipe()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Error creating stdout pipe:", err)
|
||||||
|
}
|
||||||
|
stderr, err := cmd.StderrPipe()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Error creating stderr pipe:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 开始执行命令
|
||||||
|
if err := cmd.Start(); err != nil {
|
||||||
|
log.Fatalf("Failed to start command: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用协程异步读取输出
|
||||||
|
go func() {
|
||||||
|
scanner := bufio.NewScanner(stdout)
|
||||||
|
for scanner.Scan() {
|
||||||
|
fmt.Println("STDOUT:", scanner.Text())
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
scanner := bufio.NewScanner(stderr)
|
||||||
|
for scanner.Scan() {
|
||||||
|
fmt.Println("STDERR:", scanner.Text())
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 等待命令执行完成
|
||||||
|
if err := cmd.Wait(); err != nil {
|
||||||
|
log.Printf("Command finished with error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if onComplete != nil {
|
||||||
|
onComplete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user