UI改进
This commit is contained in:
@@ -2,6 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"ffmpeg-gui/internal/config"
|
||||
"ffmpeg-gui/internal/ffmpeg"
|
||||
"ffmpeg-gui/internal/gpu"
|
||||
"ffmpeg-gui/internal/hwaccel"
|
||||
@@ -9,6 +10,8 @@ import (
|
||||
"ffmpeg-gui/internal/platform"
|
||||
"ffmpeg-gui/internal/task"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/wailsapp/wails/v2/pkg/runtime"
|
||||
@@ -17,6 +20,7 @@ import (
|
||||
// App is the main application struct. Its exported methods are bound to the frontend.
|
||||
type App struct {
|
||||
ctx context.Context
|
||||
cfg *config.Config
|
||||
exec *ffmpeg.Executor
|
||||
taskMgr *task.Manager
|
||||
hwDetect *hwaccel.Detector
|
||||
@@ -30,9 +34,31 @@ func NewApp() *App {
|
||||
// startup is called when the app starts.
|
||||
func (a *App) startup(ctx context.Context) {
|
||||
a.ctx = ctx
|
||||
a.cfg = config.Load()
|
||||
runtime.LogInfo(ctx, "[app] config loaded")
|
||||
|
||||
// Detect ffmpeg/ffprobe binaries
|
||||
bins, err := ffmpeg.Detect()
|
||||
// Detect ffmpeg/ffprobe (system → saved → bundled)
|
||||
bins, err := detectBins(a.cfg)
|
||||
if err != nil {
|
||||
// System/PATH not found — extract embedded zip if bundled
|
||||
zipData, zipErr := bundledDir.ReadFile("bundled/ffmpeg.zip")
|
||||
runtime.LogInfo(ctx, fmt.Sprintf("ffmpeg not in PATH, bundled zip: err=%v size=%d", zipErr, len(zipData)))
|
||||
if zipErr == nil {
|
||||
ffmpegPath, ffprobePath := a.cfg.ExtractZip(zipData)
|
||||
if ffmpegPath != "" {
|
||||
a.cfg.FFmpegPath = ffmpegPath
|
||||
}
|
||||
if ffprobePath != "" {
|
||||
a.cfg.FFprobePath = ffprobePath
|
||||
}
|
||||
if a.cfg.FFmpegPath != "" || a.cfg.FFprobePath != "" {
|
||||
a.cfg.Save()
|
||||
runtime.LogInfo(ctx, fmt.Sprintf("config saved with ffmpeg=%s ffprobe=%s", a.cfg.FFmpegPath, a.cfg.FFprobePath))
|
||||
}
|
||||
}
|
||||
// Retry with extracted binaries
|
||||
bins, err = detectBins(a.cfg)
|
||||
}
|
||||
if err != nil {
|
||||
runtime.LogError(ctx, fmt.Sprintf("ffmpeg detect failed: %v", err))
|
||||
return
|
||||
@@ -43,6 +69,11 @@ func (a *App) startup(ctx context.Context) {
|
||||
a.hwDetect = hwaccel.NewDetector(a.exec)
|
||||
a.taskMgr = task.NewManager(a.exec)
|
||||
|
||||
// Restore saved preferred accelerator
|
||||
if a.cfg.PreferredAccel != "" {
|
||||
a.taskMgr.SetHWAccel(a.cfg.PreferredAccel)
|
||||
}
|
||||
|
||||
a.taskMgr.SetEventCallback(func(eventType string, data any) {
|
||||
runtime.EventsEmit(ctx, eventType, data)
|
||||
})
|
||||
@@ -109,12 +140,106 @@ func (a *App) GetHardwareEncoders() ([]hwaccel.HWEncoder, error) {
|
||||
|
||||
// GetGPUInfo returns detected GPU models and their encoder capabilities.
|
||||
func (a *App) GetGPUInfo() []gpu.GPUInfo {
|
||||
if a.exec == nil {
|
||||
return nil
|
||||
}
|
||||
return gpu.DetectGPUs(a.exec)
|
||||
}
|
||||
|
||||
// detectBins finds ffmpeg/ffprobe, checking config paths first.
|
||||
func detectBins(cfg *config.Config) (ffmpeg.BinPaths, error) {
|
||||
// Try config-saved paths first
|
||||
if cfg.FFmpegPath != "" && cfg.FFprobePath != "" {
|
||||
if fileExists(cfg.FFmpegPath) && fileExists(cfg.FFprobePath) {
|
||||
return ffmpeg.BinPaths{FFmpeg: cfg.FFmpegPath, FFprobe: cfg.FFprobePath}, nil
|
||||
}
|
||||
}
|
||||
// Fall back to default detection (bundled, then PATH)
|
||||
return ffmpeg.Detect()
|
||||
}
|
||||
|
||||
func fileExists(path string) bool {
|
||||
_, err := os.Stat(path)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// ---- Config API ----
|
||||
|
||||
// GetConfig returns the current app configuration.
|
||||
func (a *App) GetConfig() map[string]string {
|
||||
return map[string]string{
|
||||
"theme": a.cfg.Theme,
|
||||
"preferredAccel": a.cfg.PreferredAccel,
|
||||
"outputDir": a.cfg.OutputDir,
|
||||
"namingRule": a.cfg.NamingRule,
|
||||
}
|
||||
}
|
||||
|
||||
// SaveConfig saves a configuration value.
|
||||
func (a *App) SaveConfig(key, value string) {
|
||||
switch key {
|
||||
case "theme":
|
||||
a.cfg.Theme = value
|
||||
case "preferredAccel":
|
||||
a.cfg.PreferredAccel = value
|
||||
case "outputDir":
|
||||
a.cfg.OutputDir = value
|
||||
case "namingRule":
|
||||
a.cfg.NamingRule = value
|
||||
}
|
||||
a.cfg.Save()
|
||||
}
|
||||
|
||||
// ---- Hardware Detection ----
|
||||
|
||||
// CheckFFmpeg returns ffmpeg and ffprobe paths and version info.
|
||||
func (a *App) CheckFFmpeg() map[string]string {
|
||||
result := map[string]string{
|
||||
"ffmpegPath": "",
|
||||
"ffprobePath": "",
|
||||
"ffmpegVer": "",
|
||||
"ffprobeVer": "",
|
||||
}
|
||||
if a.exec == nil {
|
||||
// Try config paths as fallback
|
||||
bins, err := detectBins(a.cfg)
|
||||
if err != nil {
|
||||
return result
|
||||
}
|
||||
result["ffmpegPath"] = bins.FFmpeg
|
||||
result["ffprobePath"] = bins.FFprobe
|
||||
// Can't get version without executor, but at least show paths
|
||||
return result
|
||||
}
|
||||
|
||||
// Use saved config paths if available
|
||||
if a.cfg.FFmpegPath != "" && fileExists(a.cfg.FFmpegPath) {
|
||||
result["ffmpegPath"] = a.cfg.FFmpegPath
|
||||
} else {
|
||||
bins, _ := ffmpeg.Detect()
|
||||
result["ffmpegPath"] = bins.FFmpeg
|
||||
}
|
||||
if a.cfg.FFprobePath != "" && fileExists(a.cfg.FFprobePath) {
|
||||
result["ffprobePath"] = a.cfg.FFprobePath
|
||||
} else {
|
||||
bins, _ := ffmpeg.Detect()
|
||||
result["ffprobePath"] = bins.FFprobe
|
||||
}
|
||||
|
||||
out, err := a.exec.RunSync("-version")
|
||||
if err == nil {
|
||||
lines := strings.Split(out, "\n")
|
||||
if len(lines) > 0 {
|
||||
result["ffmpegVer"] = strings.TrimSpace(lines[0])
|
||||
}
|
||||
}
|
||||
out, err = a.exec.Probe("-version")
|
||||
if err == nil {
|
||||
lines := strings.Split(out, "\n")
|
||||
if len(lines) > 0 {
|
||||
result["ffprobeVer"] = strings.TrimSpace(lines[0])
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// GetAccelerators returns detected hardware acceleration methods.
|
||||
func (a *App) GetAccelerators() ([]hwaccel.Accelerator, error) {
|
||||
if a.hwDetect == nil {
|
||||
@@ -220,6 +345,13 @@ func (a *App) SelectOutputFile(defaultName string) (string, error) {
|
||||
})
|
||||
}
|
||||
|
||||
// SelectOutputDir opens a directory dialog for selecting a default output folder.
|
||||
func (a *App) SelectOutputDir() (string, error) {
|
||||
return runtime.OpenDirectoryDialog(a.ctx, runtime.OpenDialogOptions{
|
||||
Title: "选择默认输出目录",
|
||||
})
|
||||
}
|
||||
|
||||
// SelectSubtitleFile opens a file dialog for selecting an external subtitle file.
|
||||
func (a *App) SelectSubtitleFile() (string, error) {
|
||||
return runtime.OpenFileDialog(a.ctx, runtime.OpenDialogOptions{
|
||||
|
||||
Reference in New Issue
Block a user