forked from Leak_Technologies/VideoTools
Major refactoring to improve code organization and enhance UI: Architecture: - Split monolithic main.go into modular internal/ package structure - Created internal/logging for centralized logging system - Created internal/modules for module handler functions - Created internal/ui for UI components and layouts - Created internal/utils for shared utility functions UI Enhancements: - Implemented rainbow gradient across 8 module buttons (violet→red) - Increased module button text size to 20 for better readability - Fixed text centering on module tiles - Converted Simple/Advanced mode toggle to tabs to save vertical space - Added vertical scrollbars to prevent UI overflow - Added metadata copy button (📋) to copy all metadata to clipboard Video Processing: - Fixed aspect ratio conversion to default to center-crop behavior - Added 6 aspect handling modes: Auto, Crop, Letterbox, Pillarbox, Blur Fill, Stretch - Fixed blur fill to maintain source resolution with padding (no scaling) - Ensured all FFmpeg filters produce even-numbered dimensions for H.264 Known Issues: - WMV files still produce FFmpeg error 234 during aspect conversions (requires codec-specific handling in future update) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
318 lines
8.6 KiB
Markdown
318 lines
8.6 KiB
Markdown
# Persistent Video Context Design
|
||
|
||
## Overview
|
||
Videos loaded in any module remain in memory, allowing users to seamlessly work across multiple modules without reloading. This enables workflows like: load once → convert → generate thumbnails → apply filters → inspect metadata.
|
||
|
||
## User Experience
|
||
|
||
### Video Lifecycle
|
||
1. **Load**: User selects a video in any module (Convert, Filter, etc.)
|
||
2. **Persist**: Video remains loaded when switching between modules
|
||
3. **Clear**: Video is cleared either:
|
||
- **Manual**: User clicks "Clear Video" button
|
||
- **Auto** (optional): After successful task completion when leaving a module
|
||
- **Replace**: Loading a new video replaces the current one
|
||
|
||
### UI Components
|
||
|
||
#### Persistent Video Info Bar
|
||
Display at top of application when video is loaded:
|
||
```
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ 📹 example.mp4 | 1920×1080 | 10:23 | H.264 | [Clear] [↻] │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
Shows:
|
||
- Filename (clickable to show full path)
|
||
- Resolution
|
||
- Duration
|
||
- Codec
|
||
- Clear button (unload video)
|
||
- Reload button (refresh metadata)
|
||
|
||
#### Module Video Controls
|
||
|
||
Each module shows one of two states:
|
||
|
||
**When No Video Loaded:**
|
||
```
|
||
┌────────────────────────────────┐
|
||
│ [Select Video File] │
|
||
│ or │
|
||
│ [Select from Recent ▼] │
|
||
└────────────────────────────────┘
|
||
```
|
||
|
||
**When Video Loaded:**
|
||
```
|
||
┌────────────────────────────────┐
|
||
│ ✓ Using: example.mp4 │
|
||
│ [Use Different Video] [Clear] │
|
||
└────────────────────────────────┘
|
||
```
|
||
|
||
### Workflow Examples
|
||
|
||
#### Multi-Operation Workflow
|
||
```
|
||
1. User opens Convert module
|
||
2. Loads "vacation.mp4"
|
||
3. Converts to H.265 → saves "vacation-h265.mp4"
|
||
4. Switches to Thumb module (vacation.mp4 still loaded)
|
||
5. Generates thumbnail grid → saves "vacation-grid.png"
|
||
6. Switches to Filter module (vacation.mp4 still loaded)
|
||
7. Applies color correction → saves "vacation-color.mp4"
|
||
8. Manually clicks "Clear" when done
|
||
```
|
||
|
||
#### Quick Comparison Workflow
|
||
```
|
||
1. Load video in Convert module
|
||
2. Test conversion with different settings:
|
||
- H.264 CRF 23
|
||
- H.265 CRF 28
|
||
- VP9 CRF 30
|
||
3. Compare outputs in Inspect module
|
||
4. Video stays loaded for entire comparison session
|
||
```
|
||
|
||
## Technical Implementation
|
||
|
||
### State Management
|
||
|
||
#### Current appState Structure
|
||
```go
|
||
type appState struct {
|
||
source *videoSource // Shared across all modules
|
||
convert convertConfig
|
||
player *player.Player
|
||
// ... other module states
|
||
}
|
||
```
|
||
|
||
The `source` field is already global to the app state, so it persists across module switches.
|
||
|
||
#### Video Source Structure
|
||
```go
|
||
type videoSource struct {
|
||
Path string
|
||
DisplayName string
|
||
Format string
|
||
Width int
|
||
Height int
|
||
Duration float64
|
||
VideoCodec string
|
||
AudioCodec string
|
||
Bitrate int
|
||
FrameRate float64
|
||
PreviewFrames []string
|
||
// ... other metadata
|
||
}
|
||
```
|
||
|
||
### Module Integration
|
||
|
||
#### Loading Video in Any Module
|
||
```go
|
||
func loadVideoInModule(state *appState) {
|
||
// Open file dialog
|
||
file := openFileDialog()
|
||
|
||
// Parse video metadata (ffprobe)
|
||
source := parseVideoMetadata(file)
|
||
|
||
// Set in global state
|
||
state.source = source
|
||
|
||
// Refresh UI to show video info bar
|
||
state.showVideoInfoBar()
|
||
|
||
// Update current module with loaded video
|
||
state.refreshCurrentModule()
|
||
}
|
||
```
|
||
|
||
#### Checking for Loaded Video
|
||
```go
|
||
func buildModuleView(state *appState) fyne.CanvasObject {
|
||
if state.source != nil {
|
||
// Video already loaded
|
||
return buildModuleWithVideo(state, state.source)
|
||
} else {
|
||
// No video loaded
|
||
return buildModuleVideoSelector(state)
|
||
}
|
||
}
|
||
```
|
||
|
||
#### Clearing Video
|
||
```go
|
||
func (s *appState) clearVideo() {
|
||
// Stop any playback
|
||
s.stopPlayer()
|
||
|
||
// Clear source
|
||
s.source = nil
|
||
|
||
// Clean up preview frames
|
||
if s.currentFrame != "" {
|
||
os.RemoveAll(filepath.Dir(s.currentFrame))
|
||
}
|
||
|
||
// Reset module states (optional)
|
||
s.resetModuleDefaults()
|
||
|
||
// Refresh UI
|
||
s.hideVideoInfoBar()
|
||
s.refreshCurrentModule()
|
||
}
|
||
```
|
||
|
||
### Auto-Clear Options
|
||
|
||
Add user preference for auto-clear behavior:
|
||
|
||
```go
|
||
type Preferences struct {
|
||
AutoClearVideo string // "never", "on_success", "on_module_switch"
|
||
}
|
||
```
|
||
|
||
**Options:**
|
||
- `never`: Only clear when user clicks "Clear" button
|
||
- `on_success`: Clear after successful operation when switching modules
|
||
- `on_module_switch`: Always clear when switching modules
|
||
|
||
### Video Info Bar Implementation
|
||
|
||
```go
|
||
func (s *appState) buildVideoInfoBar() fyne.CanvasObject {
|
||
if s.source == nil {
|
||
return container.NewMax() // Empty container
|
||
}
|
||
|
||
// File info
|
||
filename := widget.NewLabel(s.source.DisplayName)
|
||
filename.TextStyle = fyne.TextStyle{Bold: true}
|
||
|
||
// Video specs
|
||
specs := fmt.Sprintf("%dx%d | %s | %s",
|
||
s.source.Width,
|
||
s.source.Height,
|
||
formatDuration(s.source.Duration),
|
||
s.source.VideoCodec)
|
||
specsLabel := widget.NewLabel(specs)
|
||
|
||
// Clear button
|
||
clearBtn := widget.NewButton("Clear", func() {
|
||
s.clearVideo()
|
||
})
|
||
|
||
// Reload button (refresh metadata)
|
||
reloadBtn := widget.NewButton("↻", func() {
|
||
s.reloadVideoMetadata()
|
||
})
|
||
|
||
// Icon
|
||
icon := widget.NewIcon(theme.MediaVideoIcon())
|
||
|
||
return container.NewBorder(nil, nil,
|
||
container.NewHBox(icon, filename),
|
||
container.NewHBox(reloadBtn, clearBtn),
|
||
specsLabel,
|
||
)
|
||
}
|
||
```
|
||
|
||
### Recent Files Integration
|
||
|
||
Enhance with recent files list for quick access:
|
||
|
||
```go
|
||
func (s *appState) buildRecentFilesMenu() *fyne.Menu {
|
||
items := []*fyne.MenuItem{}
|
||
|
||
for _, path := range s.getRecentFiles() {
|
||
path := path // Capture for closure
|
||
items = append(items, fyne.NewMenuItem(
|
||
filepath.Base(path),
|
||
func() { s.loadVideoFromPath(path) },
|
||
))
|
||
}
|
||
|
||
return fyne.NewMenu("Recent Files", items...)
|
||
}
|
||
```
|
||
|
||
## Benefits
|
||
|
||
### User Benefits
|
||
- **Efficiency**: Load once, use everywhere
|
||
- **Workflow**: Natural multi-step processing
|
||
- **Speed**: No repeated file selection/parsing
|
||
- **Context**: Video stays "in focus" during work session
|
||
|
||
### Technical Benefits
|
||
- **Performance**: Single metadata parse per video load
|
||
- **Memory**: Shared video info across modules
|
||
- **Simplicity**: Consistent state management
|
||
- **Flexibility**: Easy to add new modules that leverage loaded video
|
||
|
||
## Migration Path
|
||
|
||
### Phase 1: Add Video Info Bar
|
||
- Implement persistent video info bar at top of window
|
||
- Show when `state.source != nil`
|
||
- Add "Clear" button
|
||
|
||
### Phase 2: Update Module Loading
|
||
- Check for `state.source` in each module's build function
|
||
- Show "Using: [filename]" when video is already loaded
|
||
- Add "Use Different Video" option
|
||
|
||
### Phase 3: Add Preferences
|
||
- Add auto-clear settings
|
||
- Implement auto-clear logic on module switch
|
||
- Add auto-clear on success option
|
||
|
||
### Phase 4: Recent Files
|
||
- Implement recent files tracking
|
||
- Add recent files dropdown in video selectors
|
||
- Persist recent files list
|
||
|
||
## Future Enhancements
|
||
|
||
### Multi-Video Support
|
||
For advanced users who want to work with multiple videos:
|
||
- Video tabs or dropdown selector
|
||
- "Pin" videos to keep multiple in memory
|
||
- Quick switch between loaded videos
|
||
|
||
### Batch Processing
|
||
Extend to batch operations on loaded video:
|
||
- Queue multiple operations
|
||
- Execute as single FFmpeg pass when possible
|
||
- Show operation queue in video info bar
|
||
|
||
### Workspace/Project Files
|
||
Save entire session state:
|
||
- Currently loaded video(s)
|
||
- Module settings
|
||
- Queued operations
|
||
- Allow resuming work sessions
|
||
|
||
## Implementation Checklist
|
||
|
||
- [ ] Design and implement video info bar component
|
||
- [ ] Add `clearVideo()` method to appState
|
||
- [ ] Update all module build functions to check for `state.source`
|
||
- [ ] Add "Use Different Video" buttons to modules
|
||
- [ ] Implement auto-clear preferences
|
||
- [ ] Add recent files tracking and menu
|
||
- [ ] Update Convert module (already partially implemented)
|
||
- [ ] Update other modules (Merge, Trim, Filters, etc.)
|
||
- [ ] Add keyboard shortcuts (Ctrl+W to clear video, etc.)
|
||
- [ ] Write user documentation
|
||
- [ ] Add tooltips explaining persistent video behavior
|