Implements basic fullscreen toggle functionality for immersive video playback.
Fullscreen Features:
- F11 key toggles fullscreen on/off
- ESC key exits fullscreen mode
- Window.SetFullScreen() for native fullscreen
- isFullscreen state tracking in appState
Keyboard Shortcuts:
- F11: Toggle fullscreen (globally available)
- ESC: Exit fullscreen (only when in fullscreen mode)
- Shortcuts work from any screen in the app
Implementation:
- Added isFullscreen bool to appState
- Created toggleFullscreen() method
- Global keyboard handler in runGUI()
- SetOnTypedKey handles F11 and ESC
Usage:
1. Load a video in VT_Player
2. Press F11 to enter fullscreen
3. Press F11 or ESC to exit fullscreen
Next Steps:
- Add fullscreen button to player controls
- Auto-hide controls after 3 seconds in fullscreen
- Show controls on mouse movement in fullscreen
- Double-click video to toggle fullscreen
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implements visual timeline with smooth interaction and keyframe visualization:
Timeline Widget Features:
- Custom Fyne widget in internal/ui/timeline.go
- Visual keyframe markers (yellow 1px vertical lines)
- Current position scrubber (white 2px line with circle handle)
- Progress fill showing played portion (gray)
- Mouse click/drag for smooth seeking
- In/out point marker support (for future cut functionality)
Rendering Performance:
- Cached rendering objects to minimize redraws
- Only scrubber position updates on playback
- Full redraw only on resize or keyframe data change
- Optimized for 1000+ keyframes without lag
UI Integration:
- Timeline replaces slider when keyframing mode is enabled
- Automatically loads keyframe timestamps from Index
- Integrates with existing updateProgress callback
- Maintains current time/total time labels
Technical Implementation:
- TimelineWidget extends widget.BaseWidget
- Custom renderer implements fyne.WidgetRenderer
- SetOnChange() for seek callback
- SetPosition() for playback updates
- SetKeyframes() for keyframe marker display
- Desktop mouse events for hover and drag
Visual Design:
- Dark gray background (#282828)
- Lighter gray progress fill (#3C3C3C)
- Yellow keyframe markers (#FFDC00 with transparency)
- White scrubber with circular handle
- Blue in-point marker for cuts
- Red out-point marker for cuts
References: DEV_SPEC lines 192-241
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implements comprehensive frame navigation UI and keyboard shortcuts:
Frame Navigation UI:
- Frame step buttons (←/→ icons) for single-frame stepping
- Keyframe jump buttons (⏮/⏭ icons) for I-frame navigation
- Frame counter display showing current frame number
- All navigation controls only visible in keyframing mode
- Automatic keyframe index loading when enabling frame mode
Keyboard Shortcuts:
- Left/Right arrows: step one frame backward/forward
- Up/Down arrows: jump to previous/next keyframe
- Space: play/pause toggle
- All shortcuts only active in keyframing mode
Frame Counter:
- Displays current frame number during playback
- Updates in real-time as video plays
- Shows "(KF)" suffix when on a keyframe
- Positioned next to playlist toggle button
Technical Details:
- StepFrame() method pauses playback and seeks precisely
- GetCurrentPosition() added to playSession for position queries
- Keyframe navigation uses binary search from detector.go
- All UI updates properly synchronized via Fyne.Do()
- Frame counter declared early for use in updateProgress callback
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Core implementation:
- Create internal/keyframe package with detector.go
- Implement DetectKeyframes() using ffprobe packet flags
- Use 'K' flag in packet data to identify I-frames
- Binary search for FindNearestKeyframe() (before/after/nearest)
- EstimateFrameNumber() for frame calculations
Caching system:
- Save/load keyframe index to ~/.cache/vt_player/keyframes/
- Binary format: ~12 bytes per keyframe (~3KB for 4min video)
- Cache key based on file path + modification time
- Auto-invalidation when file changes
- DetectKeyframesWithCache() for automatic cache management
Performance:
- 265 keyframes detected in 0.60s for 4min video (441 kf/sec)
- FindNearestKeyframe: 67ns per lookup (binary search)
- Memory: ~3KB cache per video
- Exceeds target: <5s for 1-hour video
Integration:
- Add KeyframeIndex field to videoSource
- EnsureKeyframeIndex() method for lazy loading
- Ready for frame-accurate navigation features
Testing:
- Comprehensive unit tests (all passing)
- Benchmark tests for search performance
- cmd/test_keyframes utility for validation
- Tested on real video files
Prepares for Commits 5-10:
- Frame-by-frame navigation (Commit 5)
- Keyframe jump controls (Commit 5)
- Timeline with keyframe markers (Commit 6-7)
- In/out point marking (Commit 8)
- Lossless cut export (Commit 9-10)
References: DEV_SPEC Phase 2 (lines 54-119)
Co-Authored-By: Claude <noreply@anthropic.com>
Icon system:
- Create internal/ui/icons.go with Material Symbols unicode constants
- Add 50+ icon constants for all player features
- Implement NewIconButton() and helper functions
- Add GetVolumeIcon() and GetPlayPauseIcon() dynamic icon helpers
UI updates:
- Replace emoji icons (▶/⏸ 🔊 ☰) with Material Icons
- Use play_arrow/pause for play button with state toggle
- Use volume_up/down/mute/off for volume with dynamic updates
- Use menu icon for playlist toggle
- Use skip_previous/skip_next for track navigation
Documentation:
- Add MATERIAL_ICONS_MAPPING.md with complete icon reference
- Document 50+ Material Icon unicode mappings
- Include download instructions for Material Symbols font
- Map all planned features to appropriate icons
Benefits:
- Professional, consistent icon design
- Industry-standard Material Design language
- Better rendering than emoji (no font fallback issues)
- Scalable unicode characters (works immediately)
- Ready for font enhancement (optional Material Symbols font)
Prepares for:
- Frame navigation icons (navigate_before/next)
- Keyframe jump icons (first_page/last_page)
- Cut tool icons (content_cut, markers)
- All features in FEATURE_ROADMAP.md
Co-Authored-By: Claude <noreply@anthropic.com>
UI improvements:
- Add menu bar at top with File, View, and Tools menus
- Move File operations (Open, Add Folder, Clear) to File menu
- Add Frame-Accurate Mode toggle in Tools menu
- Center playback controls (Prev, Play, Next) at bottom
- Move volume controls to left, playlist toggle to right
- Remove redundant top control bar for cleaner interface
- Add keyframingMode state to appState for feature toggle
Layout changes:
- Menu bar provides access to advanced features
- Main player area takes full space below menu
- Controls centered bottom like modern video players (Haruna/VLC)
- Cleaner interface suitable for basic playback or advanced editing
Prepares for:
- Frame-accurate navigation features (when keyframing enabled)
- Timeline with keyframe markers
- In/out point cutting tools
- Integration with VideoTools chapter support
Co-Authored-By: Claude <noreply@anthropic.com>
Major fixes:
- Fix drag-and-drop video loading bug (s.source was never set)
- Call switchToVideo() instead of showPlayerView() to properly initialize player state
- Show initial preview frame/thumbnail when video loads
- Improve ffprobe error messages (capture stderr for better diagnostics)
UI improvements:
- Move playlist from left to right side
- Add playlist toggle button (☰) with visibility control
- Load and display preview frame immediately when video loads
- Improve control layout with volume container
- Auto-hide playlist when only one video loaded
Documentation:
- Add FEATURE_ROADMAP.md tracking 30 planned features
- Add ICONS_NEEDED.md listing 53 required SVG icons
- Update .gitignore to exclude binaries
References: DEV_SPEC_FRAME_ACCURATE_PLAYBACK.md
Co-Authored-By: Claude <noreply@anthropic.com>
This commit implements two new features:
1. Compare Module:
- New UI module for side-by-side video comparison
- Loads two video files and displays detailed metadata comparison
- Shows format, resolution, codecs, bitrates, frame rate, color info, etc.
- Accessible via GUI module button or CLI: videotools compare <file1> <file2>
- Added formatBitrate() helper function for consistent bitrate display
2. Target File Size Encoding Mode:
- New bitrate mode "Target Size" for convert module
- Allows users to specify desired output file size (e.g., "25MB", "100MB", "8MB")
- Automatically calculates required video bitrate based on:
* Target file size
* Video duration
* Audio bitrate
* Container overhead (3% reserved)
- Implemented ParseFileSize() to parse size strings (KB, MB, GB)
- Implemented CalculateBitrateForTargetSize() for bitrate calculation
- Works in both GUI convert view and job queue execution
Additional changes:
- Updated printUsage() to include compare command
- Added compare button to module grid with pink color
- Added compareFile1 and compareFile2 to appState
- Consistent "Target Size" naming throughout (UI and code)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implements mutual exclusion between 'Convert Now' and queue processing:
Behavior:
- If queue is running: 'Convert Now' button is DISABLED
- If user tries to click 'Convert Now' while queue runs: Shows info dialog
with message and auto-adds video to queue instead
- Only one conversion method active at a time
This prevents:
- Multiple simultaneous FFmpeg processes competing for system resources
- Confusion about which conversion is running
- Queue and direct conversion interfering with each other
When queue is active:
- 'Convert Now' button: DISABLED (grey out)
- 'Add to Queue' button: ENABLED (highlighted)
- Clear UI signal: Only use queue mode for batch operations
Perfect for batch workflows where user loads multiple videos
and expects them all to process sequentially in the queue,
not spawn random direct conversions.
Implements automatic queue processing when jobs are added from the Convert
module via the 'Add to Queue' button:
Features:
- IsRunning() method added to queue package to check processing status
- 'Add to Queue' button now auto-starts queue if not already running
- Eliminates need to manually open Queue view and click 'Start Queue'
- Seamless workflow: Add video → Queue → Auto-starts conversion
Before:
1. Load video
2. Click 'Add to Queue'
3. Click 'View Queue'
4. Click 'Start Queue'
After:
1. Load video
2. Click 'Add to Queue' (auto-starts!)
3. Load next video
4. Click 'Add to Queue' (already running)
Perfect for batch operations where user loads multiple videos and expects
them to start encoding immediately.
Implements clear batch settings control for converting multiple videos:
Features:
- Settings persistence: All conversion settings automatically persist across videos
- Clear UI messaging: Explains that settings carry over between videos
- Reset button: One-click ability to reset all settings to defaults
- Batch workflow: Load video → set format/quality once → convert multiple videos
How it works:
1. User loads first video and configures settings (format, quality, codecs, etc)
2. Settings are stored in state.convert and persist across video loads
3. User can load additional videos - settings remain the same
4. When converting multiple videos, all use the same settings
5. User can change settings anytime - affects all subsequent videos
6. Reset button available to restore defaults if needed
This eliminates the need to reconfigure every video while allowing:
- Batch processing with same settings
- Individual video settings override when needed
- Clear visual indication of what's happening
Perfect for the user's workflow of converting 5 European videos to
DVD-NTSC format - set once, convert all 5!
Critical fix: When a DVD format (NTSC or PAL) is selected, now properly
override the video and audio codec to use DVD-compliant standards:
Video:
- Forces MPEG-2 codec (mpeg2video)
- NTSC: 6000k bitrate, 9000k max, gop=15
- PAL: 8000k bitrate, 9500k max, gop=12
Audio:
- Forces AC-3 codec for DVD container compatibility
- 192 kbps bitrate
- 48 kHz sample rate (DVD standard)
- Stereo channels (2)
This ensures that selecting a DVD format produces DVDStyler-compatible
MPEG files without codec errors. Previously, the code was using the
default H.264 + AAC, which caused 'unsupported audio codec' errors
when trying to write to MPEG container.
Fixes the issue where DVD conversions were failing with:
'Unsupported audio codec. Must be one of mp1, mp2, mp3, 16-bit pcm_dvd,
pcm_s16be, ac3 or dts.'
The setContent function was calling fyne.DoAndWait() from the main goroutine,
which created a deadlock. Changed to use fyne.Do() (asynchronous) to properly
marshal UI updates without blocking.
This resolves the error:
'fyne.Do[AndWait] called from main goroutine'
The async approach is correct here since we don't need to wait for the
content update to complete before continuing.
This commit includes several improvements:
Queue System Enhancements:
- Improved thread-safety in Add, Remove, Pause, Resume, Cancel operations
- Added PauseAll and ResumeAll methods for batch control
- Added MoveUp and MoveDown methods to reorder queue items
- Better handling of running job cancellation with proper state management
- Improved Copy strategy in List() to prevent race conditions
Convert Module Enhancement:
- Auto-set resolution to 720×480 when NTSC DVD format selected
- Auto-set resolution to 720×576 when PAL DVD format selected
- Auto-set framerate to 29.97fps (30) for NTSC, 25fps for PAL
- Added DVD resolution options to resolution selector dropdown
Display Server Improvements:
- Auto-detect Wayland vs X11 display servers in player controller
- Conditionally apply xdotool window placement (X11 only)
UI Improvements:
- Added Pause All, Resume All, and queue reordering buttons
- Fixed queue counter labeling (completed count display)
Added scripts folder with three convenience scripts:
• scripts/build.sh - Clean build with dependency verification
• scripts/run.sh - Run application (auto-builds if needed)
• scripts/alias.sh - Create 'VideoTools' command alias
Usage:
source scripts/alias.sh
VideoTools # Run app
VideoToolsRebuild # Force rebuild
VideoToolsClean # Clean artifacts
Fixed main.go DVD options:
• Fixed callback ordering so updateDVDOptions is called on format selection
• DVD aspect ratio selector now appears when DVD format is selected
• DVD info display shows specs for NTSC and PAL formats
• Works in both Simple and Advanced tabs
DVD options are now fully functional in the UI.
Integrated DVD-NTSC and DVD-PAL options into the Convert module's Simple and Advanced modes.
New Features:
✓ DVD-NTSC (720×480 @ 29.97fps) option in format selector
✓ DVD-PAL (720×576 @ 25.00fps) option in format selector
✓ DVD aspect ratio selector (4:3 or 16:9)
✓ Dynamic DVD options panel - appears only when DVD format selected
✓ Informative DVD specs displayed based on format selection
✓ Smart show/hide logic for DVD-specific controls
✓ Works in both Simple and Advanced mode tabs
DVD Specifications Displayed:
- NTSC: 720×480 @ 29.97fps, MPEG-2, AC-3 Stereo 48kHz
- PAL: 720×576 @ 25.00fps, MPEG-2, AC-3 Stereo 48kHz
- Bitrate ranges and compatibility info
Users can now:
1. Select DVD format from dropdown
2. Choose aspect ratio (4:3 or 16:9)
3. See relevant DVD specs and compatibility
4. Queue DVD conversion jobs
5. Process with existing queue system
🤖 Generated with Claude Code
Simplified the approach by removing complex callback logic and using a
simple 500ms timer-based update for the stats bar instead. This eliminates
threading errors completely while keeping the code straightforward.
Changes:
1. Removed queue change callback entirely
2. Added background timer that updates stats bar every 500ms
3. Removed initComplete flag (no longer needed)
4. Simplified setContent() to direct calls
5. Added onClearAll parameter to BuildQueueView()
6. Added ClearAll() method to Queue (removes all jobs)
7. Added Clear All button with DangerImportance styling in queue view
8. Clear Completed button now has LowImportance styling
This approach is much simpler: the UI just polls the queue state
periodically instead of trying to handle callbacks from goroutines.
No more threading errors, less code, easier to understand.
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
The threading errors were caused by queue callbacks triggering showMainMenu()
during app initialization, before the Fyne event loop was fully ready.
Changes:
1. Added initComplete flag to appState struct
2. Queue callback returns early if !initComplete, preventing UI updates
during initialization
3. Set initComplete=true AFTER ShowAndRun() would handle the event loop
4. Removed nested DoFromGoroutine() which was causing double-wrapping
5. Simplified setContent() to direct calls (no thread wrapping)
6. Callback properly marshals UI updates via DoFromGoroutine() after init
This ensures the queue callback only affects UI after the app is fully
initialized and the Fyne event loop is running.
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
The queue callback was triggering showMainMenu() from a goroutine (the
job processor) without marshaling to the main thread. This caused the
threading error "should have been called in fyne.Do[AndWait]".
Changes:
1. Queue callback now wraps all UI updates in app.Driver().DoFromGoroutine()
to safely marshal calls from the job processor goroutine to the main thread
2. setContent() now always uses DoFromGoroutine() to ensure thread safety
regardless of caller context. This prevents errors when called from
callbacks or other goroutines.
3. Added fallback for early initialization when app driver isn't ready yet
This ensures all UI updates happen on the main Fyne event loop thread.
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
Implemented three methods to add multiple videos to the queue:
1. **Drag from main menu**: When on the main menu, dragging multiple videos
onto the Convert tile automatically adds them all to the queue via
batchAddToQueue(). Already working - improved handling.
2. **Drag onto convert module**: When in the convert module, dragging
multiple video files now adds all of them to the queue instead of just
loading the first one. Single files are loaded as before.
3. **UI button support**: Added 'Add Multiple...' button next to 'Open File...'
to make it clear that users can load multiple files.
Changes:
- handleDrop(): Refactored to process all dropped files when in convert module
and call batchAddToQueue() for multiple videos
- buildVideoPane(): Added 'Add Multiple...' button and reorganized button
layout to show both single and batch options
This provides intuitive multi-file handling with three different workflows
for users who prefer different input methods.
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
- Remove queue loading at startup: Queue now starts completely fresh each
session. No Load() call from /tmp/videotools-queue.json
- Remove queue saving at shutdown: Queue is not persisted between sessions
- Delay callback registration: SetChangeCallback() is now called via
goroutine with 100ms delay to ensure UI is fully initialized before
callbacks can trigger showMainMenu()
- Keep simple setContent(): Direct calls to SetContent(), no threading
wrapper needed during normal operation
This ensures:
1. No threading errors on app startup
2. Clean empty queue on each new session
3. Proper initialization order preventing callback-during-init issues
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>