618 lines
17 KiB
Bash
618 lines
17 KiB
Bash
#!/bin/bash
|
||
# ===================================================================
|
||
# LT-Convert GUI - VideoTools Style Interface
|
||
# Author: LeakTechnologies
|
||
# Fyne-based GUI matching VideoTools design
|
||
# ===================================================================
|
||
|
||
# Check if Fyne is available
|
||
if ! command -v fyne &> /dev/null; then
|
||
echo "❌ Fyne not found. Please install Fyne:"
|
||
echo " go install fyne.io/fyne/v2/cmd/fyne@latest"
|
||
exit 1
|
||
fi
|
||
|
||
# Get script directory
|
||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||
source "$SCRIPT_DIR/modules/hardware.sh"
|
||
source "$SCRIPT_DIR/modules/codec.sh"
|
||
source "$SCRIPT_DIR/modules/quality.sh"
|
||
source "$SCRIPT_DIR/modules/filters.sh"
|
||
source "$SCRIPT_DIR/modules/encode.sh"
|
||
|
||
# VideoTools-style colors
|
||
GRID_COLOR="#2C3E50"
|
||
TEXT_COLOR="#FFFFFF"
|
||
ACCENT_COLOR="#00BCD4"
|
||
SUCCESS_COLOR="#4CAF50"
|
||
WARNING_COLOR="#FF9800"
|
||
ERROR_COLOR="#F44336"
|
||
|
||
# Create main GUI
|
||
create_main_gui() {
|
||
echo "🔨 Building Go GUI application..."
|
||
|
||
# Create temporary directory for build
|
||
mkdir -p /tmp/lt-gui-build
|
||
cd /tmp/lt-gui-build
|
||
|
||
cat > lt_convert_gui.go << 'EOF'
|
||
package main
|
||
|
||
import (
|
||
"fmt"
|
||
"image/color"
|
||
"os"
|
||
"path/filepath"
|
||
"strings"
|
||
|
||
"fyne.io/fyne/v2"
|
||
"fyne.io/fyne/v2/app"
|
||
"fyne.io/fyne/v2/canvas"
|
||
"fyne.io/fyne/v2/container"
|
||
"fyne.io/fyne/v2/dialog"
|
||
"fyne.io/fyne/v2/layout"
|
||
"fyne.io/fyne/v2/storage"
|
||
"fyne.io/fyne/v2/theme"
|
||
"fyne.io/fyne/v2/widget"
|
||
)
|
||
|
||
type ConversionConfig struct {
|
||
VideoFiles []string
|
||
SelectedEncoder string
|
||
SelectedQuality string
|
||
SelectedResolution string
|
||
SelectedFPS string
|
||
SelectedColor string
|
||
OutputContainer string
|
||
}
|
||
|
||
var config ConversionConfig
|
||
|
||
func main() {
|
||
config = ConversionConfig{
|
||
VideoFiles: getVideoFiles(),
|
||
SelectedEncoder: "Auto-detect",
|
||
SelectedQuality: "High Quality (CRF 18)",
|
||
SelectedResolution: "Source",
|
||
SelectedFPS: "Original",
|
||
SelectedColor: "No color correction",
|
||
OutputContainer: "mkv",
|
||
}
|
||
|
||
a := app.New()
|
||
w := a.NewWindow("LT-Convert - VideoTools Style")
|
||
w.Resize(fyne.NewSize(1000, 700))
|
||
w.SetContent(buildMainUI())
|
||
w.ShowAndRun()
|
||
}
|
||
|
||
func getVideoFiles() []string {
|
||
// Get video files from current directory
|
||
files, err := os.ReadDir(".")
|
||
if err != nil {
|
||
return []string{}
|
||
}
|
||
|
||
var videos []string
|
||
for _, file := range files {
|
||
if !file.IsDir() && isVideoFile(file.Name()) {
|
||
videos = append(videos, file.Name())
|
||
}
|
||
}
|
||
return videos
|
||
}
|
||
|
||
func isVideoFile(filename string) bool {
|
||
ext := strings.ToLower(filepath.Ext(filename))
|
||
videoExts := []string{".mp4", ".mkv", ".mov", ".avi", ".wmv", ".ts", ".m2ts", ".flv", ".webm", ".m4v"}
|
||
for _, ve := range videoExts {
|
||
if ext == ve {
|
||
return true
|
||
}
|
||
}
|
||
return false
|
||
}
|
||
|
||
func buildMainUI() fyne.CanvasObject {
|
||
// Title
|
||
title := canvas.NewText("LT-CONVERT", color.White)
|
||
title.TextStyle = fyne.TextStyle{Monospace: true, Bold: true}
|
||
title.TextSize = 28
|
||
|
||
// File list
|
||
fileLabel := widget.NewLabel("VIDEO FILES")
|
||
fileLabel.TextStyle = fyne.TextStyle{Bold: true}
|
||
|
||
var fileList *widget.List
|
||
if len(config.VideoFiles) > 0 {
|
||
fileList = widget.NewList([]string{"No video files found"}, func(i widget.ListItemID, o widget.ListItemID, v widget.ListItemID) {})
|
||
} else {
|
||
fileList = widget.NewList(config.VideoFiles, func(i widget.ListItemID, o widget.ListItemID, v widget.ListItemID) {})
|
||
}
|
||
|
||
// Settings sections
|
||
settingsContainer := buildSettingsContainer()
|
||
|
||
// Action buttons
|
||
startBtn := widget.NewButton("START CONVERSION", func() {
|
||
startConversion()
|
||
})
|
||
startBtn.Importance = widget.HighImportance
|
||
|
||
quitBtn := widget.NewButton("QUIT", func() {
|
||
os.Exit(0)
|
||
})
|
||
quitBtn.Importance = widget.LowImportance
|
||
|
||
// Layout
|
||
header := container.NewHBox(
|
||
container.NewCenter(title),
|
||
container.NewSpacer(),
|
||
quitBtn,
|
||
)
|
||
|
||
content := container.NewVBox(
|
||
header,
|
||
widget.NewSeparator(),
|
||
container.NewHSplit(
|
||
container.NewVBox(
|
||
fileLabel,
|
||
fileList,
|
||
),
|
||
settingsContainer,
|
||
),
|
||
widget.NewSeparator(),
|
||
container.NewHBox(
|
||
container.NewSpacer(),
|
||
startBtn,
|
||
),
|
||
)
|
||
|
||
return container.NewPadded(content)
|
||
}
|
||
|
||
func buildSettingsContainer() fyne.CanvasObject {
|
||
// Encoder selection
|
||
encoderLabel := widget.NewLabel("ENCODER")
|
||
encoderLabel.TextStyle = fyne.TextStyle{Bold: true}
|
||
|
||
encoderSelect := widget.NewSelect([]string{
|
||
"Auto-detect",
|
||
"NVIDIA HEVC NVENC",
|
||
"NVIDIA AV1 NVENC",
|
||
"AMD HEVC AMF",
|
||
"AMD AV1 AMF",
|
||
"Intel HEVC QSV",
|
||
"Intel AV1 QSV",
|
||
"CPU SVT-AV1",
|
||
"CPU x265 HEVC",
|
||
}, func(selected string) {
|
||
config.SelectedEncoder = selected
|
||
})
|
||
encoderSelect.SetSelected(config.SelectedEncoder)
|
||
|
||
// Quality selection
|
||
qualityLabel := widget.NewLabel("QUALITY")
|
||
qualityLabel.TextStyle = fyne.TextStyle{Bold: true}
|
||
|
||
qualitySelect := widget.NewSelect([]string{
|
||
"Source quality (bypass)",
|
||
"High Quality (CRF 18)",
|
||
"Good Quality (CRF 20)",
|
||
"DVD-NTSC Professional",
|
||
"DVD-PAL Professional",
|
||
"Custom bitrate",
|
||
}, func(selected string) {
|
||
config.SelectedQuality = selected
|
||
})
|
||
qualitySelect.SetSelected(config.SelectedQuality)
|
||
|
||
// Resolution selection
|
||
resolutionLabel := widget.NewLabel("RESOLUTION")
|
||
resolutionLabel.TextStyle = fyne.TextStyle{Bold: true}
|
||
|
||
resolutionSelect := widget.NewSelect([]string{
|
||
"Source file resolution",
|
||
"720p (1280×720)",
|
||
"1080p (1920×1080)",
|
||
"1440p (2560×1440)",
|
||
"4K (3840×2160)",
|
||
"2X Upscale",
|
||
"4X Upscale",
|
||
}, func(selected string) {
|
||
config.SelectedResolution = selected
|
||
})
|
||
resolutionSelect.SetSelected(config.SelectedResolution)
|
||
|
||
// FPS selection
|
||
fpsLabel := widget.NewLabel("FPS")
|
||
fpsLabel.TextStyle = fyne.TextStyle{Bold: true}
|
||
|
||
fpsSelect := widget.NewSelect([]string{
|
||
"Original FPS",
|
||
"60 FPS",
|
||
}, func(selected string) {
|
||
config.SelectedFPS = selected
|
||
})
|
||
fpsSelect.SetSelected(config.SelectedFPS)
|
||
|
||
// Color correction
|
||
colorLabel := widget.NewLabel("COLOR")
|
||
colorLabel.TextStyle = fyne.TextStyle{Bold: true}
|
||
|
||
colorSelect := widget.NewSelect([]string{
|
||
"No color correction",
|
||
"Fix pink skin tones (Topaz AI)",
|
||
"Warm enhancement",
|
||
"Cool enhancement",
|
||
"2000s DVD Restore",
|
||
"90s Quality Restore",
|
||
"VHS Quality Restore",
|
||
"Anime Preservation",
|
||
}, func(selected string) {
|
||
config.SelectedColor = selected
|
||
})
|
||
colorSelect.SetSelected(config.SelectedColor)
|
||
|
||
// Container selection
|
||
containerLabel := widget.NewLabel("CONTAINER")
|
||
containerLabel.TextStyle = fyne.TextStyle{Bold: true}
|
||
|
||
containerSelect := widget.NewSelect([]string{
|
||
"MKV (flexible)",
|
||
"MP4 (compatible)",
|
||
}, func(selected string) {
|
||
config.OutputContainer = strings.ToLower(strings.Fields(selected)[0])
|
||
})
|
||
containerSelect.SetSelected("MKV (flexible)")
|
||
|
||
// Layout settings in grid
|
||
settingsGrid := container.NewGridWithColumns(2,
|
||
encoderLabel, encoderSelect,
|
||
qualityLabel, qualitySelect,
|
||
resolutionLabel, resolutionSelect,
|
||
fpsLabel, fpsSelect,
|
||
colorLabel, colorSelect,
|
||
containerLabel, containerSelect,
|
||
)
|
||
|
||
return container.NewVBox(
|
||
widget.NewCard(settingsGrid),
|
||
)
|
||
}
|
||
|
||
func startConversion() {
|
||
if len(config.VideoFiles) == 0 {
|
||
dialog.ShowError("No video files found", "Please add video files to the directory and restart.")
|
||
return
|
||
}
|
||
|
||
// Show confirmation dialog
|
||
dialog.ShowConfirm("Start Conversion",
|
||
fmt.Sprintf("Convert %d file(s) with these settings?\n\nEncoder: %s\nQuality: %s\nResolution: %s\nFPS: %s\nColor: %s\nContainer: %s",
|
||
len(config.VideoFiles),
|
||
config.SelectedEncoder,
|
||
config.SelectedQuality,
|
||
config.SelectedResolution,
|
||
config.SelectedFPS,
|
||
config.SelectedColor,
|
||
config.OutputContainer),
|
||
func(confirmed bool) {
|
||
if confirmed {
|
||
runConversion()
|
||
}
|
||
}, nil)
|
||
}
|
||
|
||
func runConversion() {
|
||
// This would call the bash conversion functions
|
||
// For now, just show success message
|
||
dialog.ShowInformation("Conversion Started",
|
||
fmt.Sprintf("Converting %d file(s)...\n\nSettings applied:\n- Encoder: %s\n- Quality: %s\n- Resolution: %s\n- FPS: %s\n- Color: %s\n- Container: %s",
|
||
len(config.VideoFiles),
|
||
config.SelectedEncoder,
|
||
config.SelectedQuality,
|
||
config.SelectedResolution,
|
||
config.SelectedFPS,
|
||
config.SelectedColor,
|
||
config.OutputContainer))
|
||
}
|
||
EOF
|
||
|
||
# Check if Windows x64
|
||
detect_windows_x64() {
|
||
if [[ "$OSTYPE" == "msys" || "$OSTYPE" == "win32" ]] && [[ "$(uname -m)" == "x86_64" ]]; then
|
||
return 0
|
||
fi
|
||
return 1
|
||
}
|
||
|
||
# Pre-built Windows x64 GUI
|
||
build_windows_exe() {
|
||
if detect_windows_x64; then
|
||
echo "🎯 Detected Windows x64 - checking for pre-built GUI..."
|
||
|
||
# Check for pre-built executable
|
||
if [[ -f "$SCRIPT_DIR/lt-convert-gui.exe" ]]; then
|
||
echo "✅ Found pre-built Windows GUI executable"
|
||
return 0
|
||
else
|
||
echo "⚠️ Pre-built GUI not found, building from source..."
|
||
return 1
|
||
fi
|
||
else
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
# Build GUI from source (fallback)
|
||
build_gui_from_source() {
|
||
echo "🔨 Building GUI from source..."
|
||
|
||
cat > lt_convert_gui.go << 'ENDOFFILE'
|
||
package main
|
||
|
||
import (
|
||
"fyne.io/fyne/v2"
|
||
"fyne.io/fyne/v2/app"
|
||
"fyne.io/fyne/v2/container"
|
||
"fyne.io/fyne/v2/widget"
|
||
)
|
||
|
||
func main() {
|
||
a := app.New()
|
||
w := a.NewWindow("LT-Convert GUI")
|
||
w.Resize(fyne.NewSize(800, 600))
|
||
|
||
content := container.NewVBox(
|
||
widget.NewLabelWithStyle("LT-Convert GUI", fyne.TextStyle{Bold: true}),
|
||
widget.NewSeparator(),
|
||
widget.NewLabel("VideoTools Style Interface v1.0"),
|
||
widget.NewSeparator(),
|
||
widget.NewButton("Launch Bash Mode", func() {
|
||
w.Close()
|
||
}),
|
||
widget.NewButton("Quit", func() {
|
||
a.Quit()
|
||
}),
|
||
)
|
||
|
||
w.SetContent(content)
|
||
w.ShowAndRun()
|
||
}
|
||
ENDOFFILE
|
||
|
||
if go build -o lt-convert-gui lt_convert_gui.go 2>/dev/null; then
|
||
echo "✅ GUI built successfully"
|
||
return 0
|
||
else
|
||
echo "❌ Build failed"
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
# Unified GUI builder
|
||
build_gui() {
|
||
echo "🔨 Building GUI application..."
|
||
|
||
# Try Windows x64 pre-built first
|
||
if build_windows_exe; then
|
||
echo "🚀 Launching pre-built Windows GUI..."
|
||
cd "$SCRIPT_DIR"
|
||
./lt-convert-gui.exe
|
||
return 0
|
||
fi
|
||
|
||
# Fallback to source build
|
||
echo "📝 Building from source (fallback)..."
|
||
build_gui_from_source
|
||
|
||
if [[ $? -eq 0 ]]; then
|
||
echo "🚀 Launching GUI..."
|
||
if [[ -f "./lt-convert-gui" ]] || [[ -f "./lt-convert-gui.exe" ]]; then
|
||
echo "✅ GUI executable ready"
|
||
return 0
|
||
else
|
||
echo "❌ GUI executable not found after build"
|
||
return 1
|
||
fi
|
||
else
|
||
echo "❌ GUI build failed"
|
||
return 1
|
||
fi
|
||
}
|
||
ENDOFFILE
|
||
|
||
if go build -o lt-convert-gui lt_convert_gui.go 2>/dev/null; then
|
||
echo "✅ GUI built successfully"
|
||
return 0
|
||
else
|
||
echo "❌ Build failed"
|
||
return 1
|
||
fi
|
||
}
|
||
EOF
|
||
|
||
# Try to build
|
||
echo "Building Go application..."
|
||
if go build -o lt-convert-gui lt_convert_gui.go 2>/dev/null; then
|
||
echo "✅ GUI built successfully"
|
||
return 0
|
||
else
|
||
echo "❌ Build failed"
|
||
return 1
|
||
fi
|
||
}
|
||
GO_EOF
|
||
|
||
echo "🔨 Building GUI..."
|
||
|
||
# Build with error handling
|
||
if go build -o lt-convert-gui lt_convert_gui.go 2>build.log; then
|
||
echo "✅ GUI built successfully"
|
||
return 0
|
||
else
|
||
echo "❌ GUI build failed. Check build.log:"
|
||
cat build.log 2>/dev/null || echo "No build log created"
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
func buildSimpleUI() fyne.CanvasObject {
|
||
title := container.NewVBox(
|
||
widget.NewLabelWithStyle("LT-CONVERT", fyne.TextStyle{Bold: true, Monospace: true}),
|
||
widget.NewLabel("VideoTools Style GUI"),
|
||
)
|
||
|
||
// Encoder selection
|
||
encoderSelect := widget.NewSelect([]string{
|
||
"Auto-detect",
|
||
"NVIDIA HEVC NVENC",
|
||
"NVIDIA AV1 NVENC",
|
||
"AMD HEVC AMF",
|
||
"AMD AV1 AMF",
|
||
"Intel HEVC QSV",
|
||
"Intel AV1 QSV",
|
||
"CPU SVT-AV1",
|
||
"CPU x265 HEVC",
|
||
}, func(selected string) {
|
||
fmt.Printf("Selected encoder: %s\n", selected)
|
||
})
|
||
|
||
// Quality selection
|
||
qualitySelect := widget.NewSelect([]string{
|
||
"Source quality (bypass)",
|
||
"High Quality (CRF 18)",
|
||
"Good Quality (CRF 20)",
|
||
"DVD-NTSC Professional",
|
||
"DVD-PAL Professional",
|
||
"Custom bitrate",
|
||
}, func(selected string) {
|
||
fmt.Printf("Selected quality: %s\n", selected)
|
||
})
|
||
|
||
// Start button
|
||
startBtn := widget.NewButton("START CONVERSION WITH BASH", func() {
|
||
dialog.ShowConfirm("Start Conversion", "This will launch the bash version with your settings. Continue?", func(confirmed bool) {
|
||
if confirmed {
|
||
fmt.Println("Starting bash conversion...")
|
||
// In real implementation, this would call lt-convert.sh with saved settings
|
||
os.Exit(0)
|
||
}
|
||
}, nil)
|
||
})
|
||
startBtn.Importance = widget.HighImportance
|
||
|
||
quitBtn := widget.NewButton("QUIT", func() {
|
||
os.Exit(0)
|
||
})
|
||
|
||
return container.NewVBox(
|
||
title,
|
||
widget.NewSeparator(),
|
||
container.NewGridWithColumns(2,
|
||
widget.NewLabel("Encoder:"), encoderSelect,
|
||
widget.NewLabel("Quality:"), qualitySelect,
|
||
),
|
||
widget.NewSeparator(),
|
||
container.NewHBox(
|
||
widget.NewLabel("Click START to run bash version"),
|
||
container.NewSpacer(),
|
||
startBtn,
|
||
quitBtn,
|
||
),
|
||
)
|
||
}
|
||
GO_EOF
|
||
|
||
# Try to build
|
||
if go build -o lt-convert-gui lt_convert_gui.go 2>/dev/null; then
|
||
echo "✅ GUI built successfully"
|
||
return 0
|
||
else
|
||
echo "❌ GUI build failed"
|
||
return 1
|
||
fi
|
||
}
|
||
}
|
||
|
||
# Check dependencies
|
||
check_dependencies() {
|
||
echo "🔍 Checking dependencies..."
|
||
|
||
if ! command -v go &> /dev/null; then
|
||
echo "❌ Go not found. Please install Go:"
|
||
echo " https://golang.org/dl/"
|
||
return 1
|
||
fi
|
||
|
||
if ! command -v fyne &> /dev/null; then
|
||
echo "❌ Fyne not found. Installing Fyne..."
|
||
go install fyne.io/fyne/v2/cmd/fyne@latest
|
||
if [[ $? -ne 0 ]]; then
|
||
echo "❌ Failed to install Fyne"
|
||
return 1
|
||
fi
|
||
fi
|
||
|
||
echo "✅ Dependencies ready"
|
||
return 0
|
||
}
|
||
|
||
# Main execution
|
||
main() {
|
||
clear
|
||
cat << "EOF"
|
||
╔════════════════════════════════════════════════════════╗
|
||
║ LT-Convert GUI Mode ║
|
||
║ (VideoTools Style) ║
|
||
╚══════════════════════════════════════════════════════════════════════════════╝
|
||
|
||
Launching modern Fyne-based GUI...
|
||
EOF
|
||
|
||
# Check dependencies
|
||
if ! check_dependencies; then
|
||
echo "❌ Dependency check failed. Falling back to bash mode..."
|
||
echo "Press Enter to continue to lt-convert.sh..."
|
||
read
|
||
bash "$SCRIPT_DIR/lt-convert.sh" "$@"
|
||
return
|
||
fi
|
||
|
||
# Try to build and run GUI
|
||
if ! build_gui; then
|
||
echo "❌ GUI build failed. Falling back to bash mode..."
|
||
echo "Press Enter to continue to lt-convert.sh..."
|
||
read
|
||
bash "$SCRIPT_DIR/lt-convert.sh" "$@"
|
||
return
|
||
fi
|
||
|
||
# Run GUI with Windows x64 check
|
||
run_gui() {
|
||
cd "$SCRIPT_DIR"
|
||
if detect_windows_x64; then
|
||
# For Windows x64, check for exe
|
||
if [[ -f "./lt-convert-gui.exe" ]]; then
|
||
echo "🚀 Launching Windows GUI..."
|
||
./lt-convert-gui.exe
|
||
elif [[ -f "./lt-convert-gui" ]]; then
|
||
echo "🚀 Launching GUI..."
|
||
./lt-convert-gui
|
||
else
|
||
echo "❌ GUI executable not found"
|
||
return 1
|
||
fi
|
||
else
|
||
# Non-Windows or build failed, run bash version
|
||
echo "📋 Running bash version..."
|
||
bash "$SCRIPT_DIR/lt-convert.sh" "$@"
|
||
fi
|
||
}
|
||
}
|
||
}
|
||
|
||
# Run main function
|
||
main "$@" |