15 KiB
VideoTools Integration Guide - DVD Support & Queue System
📋 Executive Summary
This guide explains how to integrate the newly implemented DVD-NTSC encoding system with the queue-based batch processing system in VideoTools.
Status: ✅ Both systems are complete, tested, and ready for integration.
🎯 What's New
1. DVD-NTSC Encoding Package ✨
Location: internal/convert/
Provides:
- MPEG-2 video encoding (720×480 @ 29.97fps)
- AC-3 Dolby Digital audio (48 kHz stereo)
- Multi-region support (NTSC, PAL, SECAM)
- Comprehensive validation system
- FFmpeg command generation
Key Files:
types.go- VideoSource, ConvertConfig, FormatOption typesffmpeg.go- Codec mapping, video probingdvd.go- NTSC-specific encoding and validationdvd_regions.go- PAL, SECAM, and multi-region supportpresets.go- Output format definitions
2. Queue System (Already Integrated)
Location: internal/queue/queue.go
Provides:
- Job management and prioritization
- Pause/resume capabilities
- Real-time progress tracking
- Thread-safe operations
- JSON persistence
🔌 Integration Points
Point 1: Format Selection UI
Current State (main.go, line ~1394):
var formatLabels []string
for _, opt := range formatOptions { // Hardcoded in main.go
formatLabels = append(formatLabels, opt.Label)
}
formatSelect := widget.NewSelect(formatLabels, func(value string) {
for _, opt := range formatOptions {
if opt.Label == value {
state.convert.SelectedFormat = opt
outputHint.SetText(fmt.Sprintf("Output file: %s", state.convert.OutputFile()))
break
}
}
})
After Integration:
// Import the convert package
import "git.leaktechnologies.dev/stu/VT_Player/internal/convert"
// Use FormatOptions from convert package
var formatLabels []string
for _, opt := range convert.FormatOptions {
formatLabels = append(formatLabels, opt.Label)
}
formatSelect := widget.NewSelect(formatLabels, func(value string) {
for _, opt := range convert.FormatOptions {
if opt.Label == value {
state.convert.SelectedFormat = opt
outputHint.SetText(fmt.Sprintf("Output file: %s", state.convert.OutputFile()))
// NEW: Show DVD-specific options if DVD selected
if opt.Ext == ".mpg" {
showDVDOptions(state) // New function
}
break
}
}
})
Point 2: DVD-Specific Options Panel
New UI Component (main.go, after format selection):
func showDVDOptions(state *appState) {
// Show DVD-specific controls only when DVD format selected
dvdPanel := container.NewVBox(
// Aspect ratio selector
widget.NewLabel("Aspect Ratio:"),
widget.NewSelect([]string{"4:3", "16:9"}, func(val string) {
state.convert.OutputAspect = val
}),
// Interlacing mode
widget.NewLabel("Interlacing:"),
widget.NewSelect([]string{"Auto-detect", "Progressive", "Interlaced"}, func(val string) {
// Store selection
}),
// Region selector
widget.NewLabel("Region:"),
widget.NewSelect([]string{"NTSC", "PAL", "SECAM"}, func(val string) {
// Switch region presets
var region convert.DVDRegion
switch val {
case "NTSC":
region = convert.DVDNTSCRegionFree
case "PAL":
region = convert.DVDPALRegionFree
case "SECAM":
region = convert.DVDSECAMRegionFree
}
cfg := convert.PresetForRegion(region)
state.convert = cfg // Update config
}),
)
// Add to UI
}
Point 3: Validation Before Queue
Current State (main.go, line ~499):
func (s *appState) addConvertToQueue() error {
if !s.hasSource() {
return fmt.Errorf("no source video selected")
}
// ... build config and add to queue
}
After Integration:
func (s *appState) addConvertToQueue() error {
if !s.hasSource() {
return fmt.Errorf("no source video selected")
}
// NEW: Validate if DVD format selected
if s.convert.SelectedFormat.Ext == ".mpg" {
warnings := convert.ValidateDVDNTSC(s.source, s.convert)
// Show warnings dialog
if len(warnings) > 0 {
var warningText strings.Builder
warningText.WriteString("DVD Encoding Validation:\n\n")
for _, w := range warnings {
warningText.WriteString(fmt.Sprintf("[%s] %s\n", w.Severity, w.Message))
warningText.WriteString(fmt.Sprintf("Action: %s\n\n", w.Action))
}
dialog.ShowInformation("DVD Validation", warningText.String(), s.window)
}
}
// ... continue with queue addition
}
Point 4: FFmpeg Command Building
Current State (main.go, line ~810):
// Build FFmpeg arguments (existing complex logic)
args := []string{
"-y",
"-hide_banner",
// ... 180+ lines of filter and codec logic
}
After Integration (simplified):
func (s *appState) executeConvertJob(ctx context.Context, job *queue.Job, progressCallback func(float64)) error {
cfg := job.Config
inputPath := cfg["inputPath"].(string)
outputPath := cfg["outputPath"].(string)
// NEW: Use convert package for DVD
if fmt.Sprintf("%v", cfg["selectedFormat"]) == ".mpg" {
// Get video source info
src, err := convert.ProbeVideo(inputPath)
if err != nil {
return err
}
// Get config from job
convertCfg := s.convert // Already validated
// Use convert package to build args
args := convert.BuildDVDFFmpegArgs(inputPath, outputPath, convertCfg, src)
// Execute FFmpeg...
return s.executeFFmpeg(args, progressCallback)
}
// Fall back to existing logic for non-DVD formats
// ... existing code
}
Point 5: Job Configuration
Updated Job Creation (main.go, line ~530):
job := &queue.Job{
Type: queue.JobTypeConvert,
Title: fmt.Sprintf("Convert: %s", s.source.DisplayName),
InputFile: s.source.Path,
OutputFile: s.convert.OutputFile(),
Config: map[string]interface{}{
// Existing fields...
"inputPath": s.source.Path,
"outputPath": s.convert.OutputFile(),
"selectedFormat": s.convert.SelectedFormat,
"videoCodec": s.convert.VideoCodec,
"audioCodec": s.convert.AudioCodec,
"videoBitrate": s.convert.VideoBitrate,
"audioBitrate": s.convert.AudioBitrate,
"targetResolution": s.convert.TargetResolution,
"frameRate": s.convert.FrameRate,
// NEW: DVD-specific info
"isDVD": s.convert.SelectedFormat.Ext == ".mpg",
"aspect": s.convert.OutputAspect,
"dvdRegion": "NTSC", // Or PAL/SECAM
},
Priority: 5,
}
s.jobQueue.Add(job)
📊 Type Definitions to Export
Currently in internal/convert/types.go, these need to remain accessible within main.go:
// VideoSource - metadata about video file
type VideoSource struct { ... }
// ConvertConfig - encoding configuration
type ConvertConfig struct { ... }
// FormatOption - output format definition
type FormatOption struct { ... }
Import in main.go:
import "git.leaktechnologies.dev/stu/VT_Player/internal/convert"
// Then reference as:
// convert.VideoSource
// convert.ConvertConfig
// convert.FormatOption
🧪 Integration Checklist
-
Import convert package in main.go
import "git.leaktechnologies.dev/stu/VT_Player/internal/convert" -
Update format selection
- Replace
formatOptionswithconvert.FormatOptions - Add DVD option to dropdown
- Replace
-
Add DVD options panel
- Aspect ratio selector (4:3, 16:9)
- Region selector (NTSC, PAL, SECAM)
- Interlacing mode selector
-
Implement validation
- Call
convert.ValidateDVDNTSC()when DVD selected - Show warnings dialog before queueing
- Call
-
Update FFmpeg execution
- Use
convert.BuildDVDFFmpegArgs()for .mpg files - Keep existing logic for other formats
- Use
-
Test with sample videos
- Generate test .mpg from AVI/MOV/MP4
- Verify DVDStyler can import without re-encoding
- Test playback on PS2 or DVD player
-
Verify queue integration
- Create multi-video DVD job batch
- Test pause/resume with DVD jobs
- Test progress tracking
🔄 Data Flow Diagram
User Interface (main.go)
│
├─→ Select "DVD-NTSC (MPEG-2)" format
│ │
│ └─→ Show DVD options (aspect, region, etc.)
│
├─→ Click "Add to Queue"
│ │
│ ├─→ Call convert.ValidateDVDNTSC(video, config)
│ │ └─→ Return warnings/validation status
│ │
│ └─→ Create Job with config
│ └─→ queue.Add(job)
│
├─→ Queue displays job
│ │
│ └─→ User clicks "Start Queue"
│ │
│ ├─→ queue.Start()
│ │
│ └─→ For each job:
│ │
│ ├─→ convert.ProbeVideo(inputPath)
│ │ └─→ Return VideoSource
│ │
│ ├─→ convert.BuildDVDFFmpegArgs(...)
│ │ └─→ Return command args
│ │
│ └─→ Execute FFmpeg
│ └─→ Update job.Progress
│
└─→ Queue Viewer UI
│
└─→ Display progress
- Job status
- Progress %
- Pause/Resume buttons
- Cancel button
💾 Configuration Example
Full DVD-NTSC Job Configuration
{
"id": "job-dvd-001",
"type": "convert",
"title": "Convert to DVD-NTSC: movie.mp4",
"input_file": "movie.mp4",
"output_file": "movie.mpg",
"config": {
"inputPath": "movie.mp4",
"outputPath": "movie.mpg",
"selectedFormat": {
"Label": "DVD-NTSC (MPEG-2)",
"Ext": ".mpg",
"VideoCodec": "mpeg2video"
},
"isDVD": true,
"quality": "Standard (CRF 23)",
"videoCodec": "MPEG-2",
"videoBitrate": "6000k",
"targetResolution": "720x480",
"frameRate": "29.97",
"audioCodec": "AC-3",
"audioBitrate": "192k",
"audioChannels": "Stereo",
"aspect": "16:9",
"dvdRegion": "NTSC",
"dvdValidationWarnings": [
{
"severity": "info",
"message": "Input is 1920x1080, will scale to 720x480",
"action": "Will apply letterboxing to preserve 16:9 aspect"
}
]
},
"priority": 5,
"status": "pending",
"created_at": "2025-11-29T12:00:00Z"
}
🚀 Quick Start Integration
Step 1: Add Import
// At top of main.go
import (
// ... existing imports
"git.leaktechnologies.dev/stu/VT_Player/internal/convert"
)
Step 2: Replace Format Options
// OLD (around line 1394)
var formatLabels []string
for _, opt := range formatOptions {
formatLabels = append(formatLabels, opt.Label)
}
// NEW
var formatLabels []string
for _, opt := range convert.FormatOptions {
formatLabels = append(formatLabels, opt.Label)
}
Step 3: Add DVD Validation
// In addConvertToQueue() function
if s.convert.SelectedFormat.Ext == ".mpg" {
warnings := convert.ValidateDVDNTSC(s.source, s.convert)
// Show warnings if any
if len(warnings) > 0 {
// Display warning dialog
}
}
Step 4: Use Convert Package for FFmpeg Args
// In executeConvertJob()
if s.convert.SelectedFormat.Ext == ".mpg" {
src, _ := convert.ProbeVideo(inputPath)
args := convert.BuildDVDFFmpegArgs(inputPath, outputPath, s.convert, src)
} else {
// Use existing logic for other formats
}
✅ Verification Checklist
After integration, verify:
- Build succeeds:
go build . - Imports resolve: No import errors in IDE
- Format selector shows: "DVD-NTSC (MPEG-2)" option
- DVD options appear: When DVD format selected
- Validation works: Warnings shown for incompatible inputs
- Queue accepts jobs: DVD jobs can be added
- FFmpeg executes: Without errors
- Progress updates: In real-time
- Output generated: .mpg file created
- DVDStyler imports: Without re-encoding warning
- Playback works: On DVD player or PS2 emulator
🎯 Next Phase: Enhancement Ideas
Once integration is complete, consider:
-
DVD Menu Support
- Simple menu generation
- Chapter selection
- Thumbnail previews
-
Batch Region Conversion
- Convert same video to NTSC/PAL/SECAM in one batch
- Auto-detect region from source
-
Preset Management
- Save custom DVD presets
- Share presets between users
-
Advanced Validation
- Check minimum file size
- Estimate disc usage
- Warn about audio track count
-
CLI Integration
videotools dvd-encode input.mp4 output.mpg --region PAL- Batch encoding from command line
📚 Reference Documents
- DVD_IMPLEMENTATION_SUMMARY.md - Detailed DVD feature documentation
- QUEUE_SYSTEM_GUIDE.md - Complete queue system reference
- README.md - Main project overview
🆘 Troubleshooting
Issue: "undefined: convert" in main.go
Solution: Add import statement at top of main.go
import "git.leaktechnologies.dev/stu/VT_Player/internal/convert"
Issue: formatOption not found
Solution: Replace with convert.FormatOption
// Use:
opt := convert.FormatOption{...}
// Not:
opt := formatOption{...}
Issue: ConvertConfig fields missing
Solution: Update main.go convertConfig to use convert.ConvertConfig
Issue: FFmpeg command not working
Solution: Verify convert.BuildDVDFFmpegArgs() is called instead of manual arg building
Issue: Queue jobs not showing progress
Solution: Ensure progressCallback is called in executeConvertJob
progressCallback(percentComplete) // Must be called regularly
✨ Summary
The VideoTools project now has:
- ✅ Complete DVD-NTSC encoding system (internal/convert/)
- ✅ Fully functional queue system (internal/queue/)
- ✅ Integration points identified (this guide)
- ✅ Comprehensive documentation (multiple guides)
Next step: Integrate these components into main.go following this guide.
The integration is straightforward and maintains backward compatibility with existing video formats.