🎯 Consolidation Complete: - Merged 4 separate tools into 1 unified binary - 74% code reduction (2,400 → 600 lines) - 100% elimination of duplicate code - All original functionality preserved 📁 Project Structure: - gwutils/ - Shared utilities package with common functions - gwencoder/ - Unified encoder with multiple modes - Documentation and build scripts 🚀 Features: - 4 encoding modes: --fast, --web, --quick, --tiny - Unified configuration system - Consistent progress tracking - Comprehensive error handling - Cross-platform support ✅ Tested with 4K video encoding - all modes working perfectly
211 lines
5.1 KiB
Go
211 lines
5.1 KiB
Go
package gwutils
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"time"
|
|
)
|
|
|
|
// Config represents the unified configuration structure
|
|
type Config struct {
|
|
LastCodec string `json:"last_codec"`
|
|
LastAudioCodec string `json:"last_audio_codec"`
|
|
LastContainer string `json:"last_container"`
|
|
LastResolution string `json:"last_resolution"`
|
|
LastCRF string `json:"last_crf"`
|
|
LastPreset string `json:"last_preset"`
|
|
LastBitrate string `json:"last_bitrate"`
|
|
ExtractSubtitles bool `json:"extract_subtitles"`
|
|
AudioBitrate int `json:"audio_bitrate"`
|
|
}
|
|
|
|
// EncodingMode represents a predefined encoding configuration
|
|
type EncodingMode struct {
|
|
Name string `json:"name"`
|
|
Description string `json:"description"`
|
|
Codec string `json:"codec"`
|
|
CRF string `json:"crf"`
|
|
Preset string `json:"preset"`
|
|
Bitrate string `json:"bitrate"`
|
|
AudioCodec string `json:"audio_codec"`
|
|
Container string `json:"container"`
|
|
Resolution string `json:"resolution"`
|
|
Maxrate string `json:"maxrate"`
|
|
AudioBitrate string `json:"audio_bitrate"`
|
|
}
|
|
|
|
// Preset represents a predefined encoding preset
|
|
type Preset struct {
|
|
Name string
|
|
Description string
|
|
Codec string
|
|
CRF string
|
|
Preset string
|
|
Bitrate string
|
|
AudioCodec string
|
|
Container string
|
|
Resolution string
|
|
}
|
|
|
|
// ProgressTracker tracks encoding progress
|
|
type ProgressTracker struct {
|
|
StartTime time.Time
|
|
TotalDuration float64
|
|
LastUpdate time.Time
|
|
LastProgress float64
|
|
}
|
|
|
|
// DefaultConfig returns a default configuration
|
|
func DefaultConfig() Config {
|
|
return Config{
|
|
LastCodec: "1",
|
|
LastAudioCodec: "1",
|
|
LastContainer: "1",
|
|
LastResolution: "original",
|
|
LastCRF: "28",
|
|
LastPreset: "medium",
|
|
LastBitrate: "2000",
|
|
ExtractSubtitles: true,
|
|
AudioBitrate: 64,
|
|
}
|
|
}
|
|
|
|
// LoadConfig loads configuration from file
|
|
func LoadConfig(filename string) Config {
|
|
config := DefaultConfig()
|
|
|
|
data, err := os.ReadFile(filename)
|
|
if err != nil {
|
|
return config
|
|
}
|
|
|
|
if err := json.Unmarshal(data, &config); err != nil {
|
|
fmt.Printf("Error parsing config file: %v\n", err)
|
|
return DefaultConfig()
|
|
}
|
|
|
|
return config
|
|
}
|
|
|
|
// SaveConfig saves configuration to file
|
|
func SaveConfig(config Config, filename string) error {
|
|
data, err := json.MarshalIndent(config, "", " ")
|
|
if err != nil {
|
|
return fmt.Errorf("error marshaling config: %v", err)
|
|
}
|
|
|
|
if err := os.WriteFile(filename, data, 0644); err != nil {
|
|
return fmt.Errorf("error writing config file: %v", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetDefaultPresets returns the default encoding presets
|
|
func GetDefaultPresets() []Preset {
|
|
return []Preset{
|
|
{
|
|
Name: "Fast AV1",
|
|
Description: "Quick AV1 encoding for web/streaming",
|
|
Codec: "1",
|
|
CRF: "30",
|
|
Preset: "10",
|
|
Bitrate: "2000",
|
|
AudioCodec: "1",
|
|
Container: "1",
|
|
Resolution: "720",
|
|
},
|
|
{
|
|
Name: "Quality AV1",
|
|
Description: "High-quality AV1 for archival",
|
|
Codec: "1",
|
|
CRF: "20",
|
|
Preset: "6",
|
|
Bitrate: "4000",
|
|
AudioCodec: "1",
|
|
Container: "1",
|
|
Resolution: "1080",
|
|
},
|
|
{
|
|
Name: "Fast HEVC",
|
|
Description: "Quick HEVC encoding with hardware acceleration",
|
|
Codec: "2",
|
|
CRF: "28",
|
|
Preset: "p7",
|
|
Bitrate: "2000",
|
|
AudioCodec: "1",
|
|
Container: "1",
|
|
Resolution: "720",
|
|
},
|
|
{
|
|
Name: "Quality HEVC",
|
|
Description: "High-quality HEVC with hardware acceleration",
|
|
Codec: "2",
|
|
CRF: "18",
|
|
Preset: "p4",
|
|
Bitrate: "4000",
|
|
AudioCodec: "1",
|
|
Container: "1",
|
|
Resolution: "merge",
|
|
},
|
|
{
|
|
Name: "Universal H264",
|
|
Description: "Compatible H264 for all devices",
|
|
Codec: "3",
|
|
CRF: "23",
|
|
Preset: "medium",
|
|
Bitrate: "2500",
|
|
AudioCodec: "2",
|
|
Container: "2",
|
|
Resolution: "720",
|
|
},
|
|
}
|
|
}
|
|
|
|
// GetDefaultModes returns the default encoding modes for quick/template functionality
|
|
func GetDefaultModes() map[string]EncodingMode {
|
|
return map[string]EncodingMode{
|
|
"web": {
|
|
Name: "Web-optimized",
|
|
Description: "Web-optimized encoding (WEBM, CRF 40, Opus 64kbps, preset 10)",
|
|
Codec: "av1",
|
|
CRF: "40",
|
|
Preset: "10",
|
|
AudioBitrate: "64",
|
|
Container: "webm",
|
|
Maxrate: "2000",
|
|
},
|
|
"quick": {
|
|
Name: "Quick",
|
|
Description: "Quick encoding (MKV, CRF 32, Opus 80kbps, preset 10)",
|
|
Codec: "av1",
|
|
CRF: "32",
|
|
Preset: "10",
|
|
AudioBitrate: "80",
|
|
Container: "mkv",
|
|
Maxrate: "3000",
|
|
},
|
|
"tiny": {
|
|
Name: "Tiny",
|
|
Description: "Tiny encoding (MP4, CRF 45, Opus 64kbps, preset 8)",
|
|
Codec: "av1",
|
|
CRF: "45",
|
|
Preset: "8",
|
|
AudioBitrate: "64",
|
|
Container: "mp4",
|
|
Maxrate: "1500",
|
|
},
|
|
"fast": {
|
|
Name: "Fast",
|
|
Description: "Fast AV1 encoding (MKV, CRF 32, Opus 64kbps, preset 10)",
|
|
Codec: "av1",
|
|
CRF: "32",
|
|
Preset: "10",
|
|
AudioBitrate: "64",
|
|
Container: "mkv",
|
|
Maxrate: "2000",
|
|
},
|
|
}
|
|
}
|