Created step-by-step guide for modularizing main.go: - 9 modules to extract in priority order (least → most important) - Filters first (easiest), Convert last (most critical) - Clear instructions for each extraction - Reference patterns from existing modules - Success criteria and testing steps - Emergency rollback procedures Module extraction order: 1. filters_module.go (~236 lines) - Simple UI 2. upscale_module.go (~1200 lines) - AI integration 3. thumb_module.go (~400 lines) - Thumbnail generation 4. player_module.go (~800 lines) - Playback system 5. compare_module.go (~550 lines) - File comparison 6. merge_module.go (~700 lines) - File merging 7. benchmark_module.go (~400 lines) - Encoder benchmarks 8. queue_module.go (~500 lines) - Job queue 9. convert_module.go (~6400 lines) - Most critical, do last Goal: Reduce main.go from 14,116 → ~4,000 lines Expected: Windows build time 5+ min → <2 min Each module extraction is independent and testable. Pattern established with inspect_module.go (already done). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
412 lines
11 KiB
Markdown
412 lines
11 KiB
Markdown
# Main.go Modularization - Incremental Refactoring Prompt
|
|
|
|
## Current State
|
|
|
|
**Problem:** main.go is 14,116 lines (420KB) causing:
|
|
- 5+ minute builds on Windows
|
|
- Poor code maintainability
|
|
- Difficult to navigate and modify
|
|
|
|
**Goal:** Extract modules into separate files following established patterns
|
|
|
|
**Already Extracted:**
|
|
- ✅ `author_module.go` (1,421 lines) - Pattern reference
|
|
- ✅ `subtitles_module.go` (756 lines) - Pattern reference
|
|
- ✅ `inspect_module.go` (292 lines) - Pattern reference
|
|
|
|
**Remaining:** 9 modules to extract (~10,000 lines)
|
|
|
|
---
|
|
|
|
## Extraction Order (Least Important → Most Important)
|
|
|
|
Work through these **one module at a time**, testing after each extraction:
|
|
|
|
### Phase 1: Simple Modules (Start Here)
|
|
|
|
1. **filters_module.go** (~236 lines)
|
|
- Lines: ~13261-13497
|
|
- Functions: `showFiltersView()`, `buildFiltersView()`
|
|
- State fields: `filtersFile`, `filterBrightness`, `filterContrast`, etc.
|
|
- Why first: Simple UI-only, no job execution
|
|
|
|
2. **upscale_module.go** (~889 lines + job executor)
|
|
- Lines: ~13498-14387 + job executor at ~4568-5023
|
|
- Functions: `showUpscaleView()`, `buildUpscaleView()`, `executeUpscaleJob()`, AI helpers
|
|
- State fields: All `upscale*` fields (~25 fields)
|
|
- Includes: AI backend detection, model helpers
|
|
|
|
3. **thumb_module.go** (~335 lines + job executor)
|
|
- Lines: ~12837-13172 + executeThumbJob at ~4222-4264
|
|
- Functions: `showThumbView()`, `buildThumbView()`, `executeThumbJob()`
|
|
- State fields: `thumbFile`, `thumbCount`, `thumbWidth`, etc.
|
|
|
|
### Phase 2: Moderate Complexity
|
|
|
|
4. **player_module.go** (~87 lines + playSession ~450 lines)
|
|
- Lines: ~13173-13260 + playSession at ~8929-9329
|
|
- Functions: `showPlayerView()`, `buildPlayerView()`, `stopPlayer()`, entire `playSession` type
|
|
- State fields: `playerFile`, `player`, `playerReady`, `playerVolume`, etc.
|
|
- Types: `playSession`, `playerSurface`, `playSession` methods
|
|
- Note: Includes recent performance improvements
|
|
|
|
5. **compare_module.go** (~503 lines + fullscreen)
|
|
- Lines: ~12068-12571 + fullscreen at ~14277-14387
|
|
- Functions: `showCompareView()`, `buildCompareView()`, `buildCompareFullscreenView()`, `showCompareFullscreen()`
|
|
- State fields: `compareFile1`, `compareFile2`, `autoCompare`
|
|
|
|
### Phase 3: Complex Infrastructure
|
|
|
|
6. **merge_module.go** (~374 lines + job executor)
|
|
- Lines: ~2752-3126 + executeMergeJob at ~3232-3564
|
|
- Functions: `showMergeView()` (inline UI builder), `addMergeToQueue()`, `executeMergeJob()`
|
|
- State fields: `mergeClips`, `mergeFormat`, `mergeOutput`, etc.
|
|
- Types: `mergeClip` struct
|
|
- Note: UI builder is inline, needs extraction to `buildMergeView()`
|
|
|
|
7. **benchmark_module.go** (~326 lines)
|
|
- Lines: ~1938-2264 + config persistence at ~700-729
|
|
- Functions: `showBenchmark()`, `showBenchmarkHistory()`, benchmark helpers
|
|
- Types: `benchmarkConfig`, `benchmarkRun`
|
|
|
|
8. **queue_module.go** (~scattered functions)
|
|
- Functions: `showQueue()`, `refreshQueueView()`, `jobExecutor()`, `buildFFmpegCommandFromJob()`
|
|
- Lines: Various, needs gathering
|
|
|
|
### Phase 4: Most Critical (Do Last)
|
|
|
|
9. **convert_module.go** (~6,384 lines - THE BIG ONE)
|
|
- Lines: ~5683-12067
|
|
- Functions: 50+ functions including `buildConvertView()`, `showConvertView()`, all convert helpers
|
|
- State fields: All `convert*` fields, `source`, `loadedVideos`, etc.
|
|
- Types: `convertConfig`, `formatOption`
|
|
- Shared components: `buildMetadataPanel()`, `buildVideoPane()` (used by other modules)
|
|
- May need to split into multiple files if > 3000 lines
|
|
|
|
---
|
|
|
|
## Instructions for Each Module Extraction
|
|
|
|
### Step-by-Step Process
|
|
|
|
For each module (do ONE at a time):
|
|
|
|
#### 1. Create the Module File
|
|
|
|
**File:** `{module}_module.go` (e.g., `filters_module.go`)
|
|
|
|
**Template:**
|
|
```go
|
|
package main
|
|
|
|
import (
|
|
// Copy imports from main.go that this module needs
|
|
"fyne.io/fyne/v2"
|
|
"fyne.io/fyne/v2/container"
|
|
// ... etc
|
|
)
|
|
|
|
// Move the show{Module}View() method here
|
|
func (s *appState) show{Module}View() {
|
|
s.stopPreview()
|
|
s.lastModule = s.active
|
|
s.active = "{module}"
|
|
s.setContent(build{Module}View(s))
|
|
}
|
|
|
|
// Move the build{Module}View() function here
|
|
func build{Module}View(state *appState) fyne.CanvasObject {
|
|
// ... entire function body ...
|
|
}
|
|
|
|
// Move any helper functions specific to this module
|
|
// Move any job executor functions (execute{Module}Job)
|
|
```
|
|
|
|
#### 2. Remove from main.go
|
|
|
|
Delete the exact same functions you just moved. Use line numbers as guide.
|
|
|
|
**IMPORTANT:** Do NOT remove:
|
|
- Shared functions used by multiple modules (e.g., `moduleColor()`, `probeVideo()`)
|
|
- State fields in `appState` struct (leave all state in main.go)
|
|
- Type definitions used across modules
|
|
|
|
#### 3. Build and Test
|
|
|
|
```bash
|
|
# Must succeed
|
|
go build -o VideoTools .
|
|
|
|
# Check line count reduction
|
|
wc -l main.go {module}_module.go
|
|
|
|
# Test the module works
|
|
./VideoTools # Navigate to the module and test basic functionality
|
|
```
|
|
|
|
#### 4. Commit
|
|
|
|
```bash
|
|
git add {module}_module.go main.go
|
|
git commit -m "Extract {module} module from main.go
|
|
|
|
- Create {module}_module.go (XXX lines)
|
|
- Move show{Module}View() and build{Module}View()
|
|
- Reduce main.go by XXX lines
|
|
- All tests pass, module functions correctly
|
|
"
|
|
```
|
|
|
|
#### 5. Move to Next Module
|
|
|
|
Repeat the process for the next module in the order above.
|
|
|
|
---
|
|
|
|
## Reference Patterns
|
|
|
|
Look at these existing extracted modules for the pattern:
|
|
|
|
### Pattern 1: Simple Module (inspect_module.go)
|
|
```go
|
|
package main
|
|
|
|
import (
|
|
// Necessary imports
|
|
)
|
|
|
|
func (s *appState) showInspectView() {
|
|
s.stopPreview()
|
|
s.lastModule = s.active
|
|
s.active = "inspect"
|
|
s.setContent(buildInspectView(s))
|
|
}
|
|
|
|
func buildInspectView(state *appState) fyne.CanvasObject {
|
|
// UI building code
|
|
}
|
|
```
|
|
|
|
### Pattern 2: Module with Job Executor (subtitles_module.go)
|
|
```go
|
|
package main
|
|
|
|
import (
|
|
// Necessary imports
|
|
)
|
|
|
|
func (s *appState) showSubtitlesView() {
|
|
// ...
|
|
}
|
|
|
|
func buildSubtitlesView(state *appState) fyne.CanvasObject {
|
|
// ...
|
|
}
|
|
|
|
func (s *appState) executeSubtitlesJob(ctx context.Context, job *queue.Job, progressCallback func(float64)) error {
|
|
// Job execution logic
|
|
}
|
|
|
|
// Helper functions specific to subtitles
|
|
```
|
|
|
|
### Pattern 3: Module with Types (author_module.go)
|
|
```go
|
|
package main
|
|
|
|
import (
|
|
// ...
|
|
)
|
|
|
|
// Types specific to this module
|
|
type authorClip struct {
|
|
// ...
|
|
}
|
|
|
|
func (s *appState) showAuthorView() {
|
|
// ...
|
|
}
|
|
|
|
// Many helper functions specific to author module
|
|
```
|
|
|
|
---
|
|
|
|
## What Stays in main.go
|
|
|
|
**DO NOT MOVE these to module files:**
|
|
|
|
### Core Application
|
|
- `main()` function
|
|
- `runGUI()` function
|
|
- `type appState struct` definition (all fields stay)
|
|
- Window setup and initialization
|
|
|
|
### Shared Utilities (Used by 3+ modules)
|
|
- `moduleColor()` - Color scheme helper
|
|
- `moduleFooter()` - Footer bar builder
|
|
- `statusStrip()` - Status bar
|
|
- `probeVideo()` - Video file analysis (CRITICAL - used everywhere)
|
|
- `buildMetadataPanel()` - Used by convert and other modules
|
|
- `buildVideoPane()` - Video preview pane (used by many modules)
|
|
- `formatBitrate()`, `formatClock()`, etc. - Format helpers
|
|
|
|
### Navigation & State
|
|
- `showMainMenu()` - Main menu builder
|
|
- `setContent()` - Content switcher
|
|
- History sidebar code
|
|
- Stats bar updates
|
|
- Drop handlers
|
|
|
|
### Shared Types
|
|
- `videoSource` type and methods
|
|
- Shared constants and enums
|
|
|
|
---
|
|
|
|
## Special Cases & Gotchas
|
|
|
|
### Convert Module (Do Last!)
|
|
|
|
The convert module is massive and may need splitting:
|
|
|
|
**Option A: Single File** (if < 3000 lines after extraction)
|
|
- `convert_module.go`
|
|
|
|
**Option B: Split into Multiple Files** (if > 3000 lines)
|
|
- `convert_module.go` - Main UI and show function
|
|
- `convert_config.go` - Configuration types and persistence
|
|
- `convert_job.go` - Job execution logic
|
|
- `convert_helpers.go` - Codec/format helpers
|
|
|
|
**Shared Components:**
|
|
- `buildMetadataPanel()` and `buildVideoPane()` might need to move to `shared_ui.go` if used by many modules
|
|
|
|
### Merge Module Inline UI
|
|
|
|
The merge module has UI building inline in `showMergeView()`. You'll need to:
|
|
|
|
1. Extract inline UI to `buildMergeView()` function
|
|
2. Then follow standard pattern
|
|
|
|
### Player Module with playSession
|
|
|
|
The `playSession` type and all its methods should move to `player_module.go` together.
|
|
|
|
### Import Adjustments
|
|
|
|
Some modules might need internal package imports that aren't in main.go:
|
|
- Check for missing imports after extraction
|
|
- Run `go build` to catch import errors
|
|
- Add necessary imports to the module file
|
|
|
|
---
|
|
|
|
## Success Criteria for Each Extraction
|
|
|
|
Before moving to next module, verify:
|
|
|
|
- ✅ `go build` succeeds with no errors
|
|
- ✅ Module file has clear structure (show function, build function, helpers)
|
|
- ✅ main.go reduced by expected lines
|
|
- ✅ Module works when tested in GUI
|
|
- ✅ No duplicate code between main.go and module file
|
|
- ✅ Git commit created with clear message
|
|
- ✅ Can navigate to module and use basic features
|
|
|
|
---
|
|
|
|
## Tracking Progress
|
|
|
|
After each extraction, update this checklist:
|
|
|
|
- [ ] 1. filters_module.go (~236 lines)
|
|
- [ ] 2. upscale_module.go (~1200 lines)
|
|
- [ ] 3. thumb_module.go (~400 lines)
|
|
- [ ] 4. player_module.go (~800 lines)
|
|
- [ ] 5. compare_module.go (~550 lines)
|
|
- [ ] 6. merge_module.go (~700 lines)
|
|
- [ ] 7. benchmark_module.go (~400 lines)
|
|
- [ ] 8. queue_module.go (~500 lines)
|
|
- [ ] 9. convert_module.go (~6400 lines - may split)
|
|
|
|
**Target:** main.go from 14,116 lines → ~4,000 lines
|
|
|
|
---
|
|
|
|
## Expected Timeline
|
|
|
|
- **Simple modules (1-3):** 20-30 min each
|
|
- **Moderate modules (4-5):** 30-45 min each
|
|
- **Complex modules (6-8):** 45-60 min each
|
|
- **Convert module (9):** 2-3 hours (largest, most critical)
|
|
|
|
**Total:** ~8-12 hours of incremental work
|
|
|
|
---
|
|
|
|
## Final Build Performance Goal
|
|
|
|
After all extractions:
|
|
|
|
**Before:**
|
|
- main.go: 14,329 lines (426KB)
|
|
- Build time (Windows): 5+ minutes
|
|
|
|
**After:**
|
|
- main.go: ~4,000 lines (~120KB)
|
|
- Module files: 10-12 files (~800-1500 lines each)
|
|
- Build time (Windows): < 2 minutes (hopefully < 1 minute)
|
|
|
|
---
|
|
|
|
## Questions to Ask If Stuck
|
|
|
|
1. **"Does this function use state from multiple modules?"**
|
|
- Yes → Keep in main.go
|
|
- No → Move to module file
|
|
|
|
2. **"Is this function called by 3+ different modules?"**
|
|
- Yes → Keep in main.go as shared utility
|
|
- No → Move to the module that uses it
|
|
|
|
3. **"Is this a type used across modules?"**
|
|
- Yes → Keep type definition in main.go
|
|
- No → Move to module file
|
|
|
|
4. **"Where are the line numbers for this module?"**
|
|
- Check this document's module listing
|
|
- Use grep to find function: `grep -n "func buildXView" main.go`
|
|
|
|
---
|
|
|
|
## Emergency Rollback
|
|
|
|
If an extraction breaks the build:
|
|
|
|
```bash
|
|
# Restore main.go from backup
|
|
git checkout HEAD -- main.go
|
|
|
|
# Remove the broken module file
|
|
rm {module}_module.go
|
|
|
|
# Try again with more care
|
|
```
|
|
|
|
---
|
|
|
|
## Start Here
|
|
|
|
**First module to extract:** `filters_module.go`
|
|
|
|
1. Find lines ~13261-13497 in main.go
|
|
2. Create `filters_module.go`
|
|
3. Copy `showFiltersView()` and `buildFiltersView()`
|
|
4. Remove from main.go
|
|
5. Build, test, commit
|
|
6. Move to next module
|
|
|
|
Good luck! Take it one module at a time, test after each extraction, and the convert module will be much easier once you've established the pattern with the simpler modules.
|