Commit Graph

837 Commits

Author SHA1 Message Date
7e252fbeff Keep seek/step playback state consistent 2026-01-10 04:39:36 -05:00
5b936f4cab Pull preroll frames when paused 2026-01-10 03:22:34 -05:00
2debc079fc Render frame immediately on step seek 2026-01-10 03:18:42 -05:00
407269b6c7 Improve GStreamer frame stepping reliability 2026-01-10 03:08:18 -05:00
48fc94acb8 Prime GStreamer frames after seeks 2026-01-10 03:05:07 -05:00
15b09ee9df Fix GStreamer appsink timeout type 2026-01-10 02:54:32 -05:00
9ff49460e2 Fix GStreamer message type check 2026-01-10 02:53:06 -05:00
3f1fe74a9a Harden GStreamer playback pacing and errors 2026-01-10 02:50:38 -05:00
3d7cbfa22e Clear player session on video reset 2026-01-10 02:46:10 -05:00
9afe4251df fix(author): remove quotes from fontfile and font paths in FFmpeg filter
Fixed FFmpeg filter parsing error by removing single quotes from font
paths and properly escaping special characters instead.

The Bug:
FFmpeg's drawtext filter was failing because fontfile paths were wrapped
in single quotes, which conflicted with the filter expression parsing:
  drawtext=fontfile='/path/to/font.ttf':text='Some Text'
                    ^                ^      ^          ^
                    These quotes broke FFmpeg's parser!

Error was:
  [AVFilterGraph] No option name near ' I Fucked My Best…'
  Error parsing filterchain ... around:

The Fix:
Before: fontfile='/home/.../IBMPlexMono-Regular.ttf'
After:  fontfile=/home/.../IBMPlexMono-Regular.ttf (with : and ' escaped)

Now we:
1. Escape : as \: (FFmpeg filter requirement)
2. Escape ' as \' (FFmpeg filter requirement)
3. Don't wrap in quotes (FFmpeg doesn't need them)

Same fix applied to font= paths (system fonts).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-10 02:02:59 -05:00
7c4bae1e2a fix(author): remove quotes from scale expression in menu logo filter
Fixed FFmpeg filter parsing error by removing unnecessary single quotes
from the scale expression in resolveMenuLogoScaleExpr.

The Bug:
FFmpeg command was failing with "Error parsing filterchain" because
the scale expression had quotes around the min() functions:
  scale=w='min(iw*0.50,180)':h='min(ih*0.50,120)'
                ^^^^^^^^^^^^        ^^^^^^^^^^^^
FFmpeg's filter parser doesn't expect quotes here and treats them as
part of the option value, breaking the parsing.

The Fix:
  Before: scale=w='min(iw*%.2f,%.0f)':h='min(ih*%.2f,%.0f)'
  After:  scale=w=min(iw*%.2f,%.0f):h=min(ih*%.2f,%.0f)

Error was:
  [AVFilterGraph] No option name near ' I Fucked My Best…'
  [AVFilterGraph] Error parsing filterchain around: ,180)':h='min...
  exit status 234

Now the scale expression is parsed correctly and menu generation
should complete successfully.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-10 01:54:47 -05:00
863d92dca4 fix(author): pass logFn through all menu generation functions
Fixed menu generation logging by threading logFn parameter through all
menu building functions. Previously, all menu FFmpeg commands were
called with logFn=nil, so errors were never logged.

Functions updated to accept and pass logFn:
- buildMenuBackground
- buildDarkMenuBackground
- buildPosterMenuBackground
- buildMenuOverlays
- buildMenuOverlay (helper)
- buildMenuMPEG

All three menu templates now properly log:
- SimpleMenu.Generate() → buildMenuBackground → runCommandWithLogger
- DarkMenu.Generate() → buildDarkMenuBackground → runCommandWithLogger
- PosterMenu.Generate() → buildPosterMenuBackground → runCommandWithLogger

Before:
- Menu FFmpeg commands run silently (logFn=nil)
- Errors not logged → "ERROR: FFmpeg failed during DVD encoding"
- No way to diagnose what failed

After:
- >> /usr/bin/ffmpeg [full command with args]
- ERROR starting command: [error details]
- ERROR command failed: [error] (exit code: X)

Combined with the previous commit's enhanced runCommandWithLogger,
we now have complete visibility into menu generation failures.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-10 01:46:01 -05:00
fbf93fc9a3 Scope history clear to active tab 2026-01-10 01:39:57 -05:00
c72443b3c6 Keep history tab selection and add clear all 2026-01-10 01:36:17 -05:00
9922738f7c debug(author): add detailed command logging to runCommandWithLogger
Enhanced runCommandWithLogger to log:
1. The exact command being executed (before execution)
2. Any errors when starting the command
3. Exit codes when commands fail

Before:
- No visibility into what command failed
- Silent failures during menu generation
- Log just showed "ERROR: FFmpeg failed during DVD encoding"

After:
- >> /usr/bin/ffmpeg [args...] (shows exact command)
- ERROR starting command: [error] (if command won't start)
- ERROR command failed: [error] (exit code: X) (if command fails)

This will help diagnose the menu generation failures by showing exactly
what FFmpeg command is failing and why.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-10 01:33:47 -05:00
3cee42d041 Fix history sidebar titles and delete handling 2026-01-10 01:28:09 -05:00
e6d23ef427 feat(author): bundle IBM Plex Mono font for DVD menus
Downloaded and bundled IBM Plex Mono Regular font (153KB) to ensure
consistent DVD menu branding across all systems.

Font details:
- Source: IBM Plex (open source, SIL Open Font License)
- File: assets/fonts/IBMPlexMono-Regular.ttf
- Size: 153KB
- Format: TrueType Font

The font is automatically discovered by findMenuFontPath() which searches:
1. assets/fonts/IBMPlexMono-Regular.ttf (working directory)
2. <executable_dir>/assets/fonts/IBMPlexMono-Regular.ttf

The menuFontArg() function now:
1. Checks if FontPath exists → uses bundled IBM Plex Mono 
2. Falls back to system fonts if bundled font missing
3. Ultimate fallback: "monospace"

This ensures VideoTools DVD menus always use the perfect VT aesthetic
with IBM Plex Mono, regardless of system-installed fonts.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-10 01:27:20 -05:00
743a6ab82f fix(author): improve DVD menu font fallback to prevent FFmpeg failures
The menu generation was failing because it tried to use "IBM Plex Mono"
font which isn't universally available. FFmpeg's drawtext filter would
fail silently when the font didn't exist.

Changes:
- Check if FontPath actually exists before using it (os.Stat check)
- Only use FontName if it's a known universally available font
- Whitelist of safe fonts: DejaVu, Liberation, Free fonts
- Ultimate fallback: "monospace" (most universally available)

Before:
- Tried IBM Plex Mono (not installed) → FFmpeg fails → "ERROR: FFmpeg failed during DVD encoding"

After:
- Tries IBM Plex Mono font file → doesn't exist
- Checks if "IBM Plex Mono" is in safe list → not in list
- Falls back to "monospace" → works everywhere

This fixes the cryptic "FFmpeg failed during DVD encoding" error that
actually occurred during menu generation, not encoding.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-10 01:22:05 -05:00
b6dbcde177 feat(author): add timestamp prefix to log filenames for chronological sorting
Changed log filename format from:
  ifuckedmybestfriendsgirlfriend-author.videotools.log

To:
  20260109_220355-ifuckedmybestfriendsgirlfriend-author.videotools.log

Benefits:
- Chronological sorting: Files sort by creation time in file browsers
- No overwrites: Each authoring job creates a unique log file
- Easy tracking: Timestamp shows exactly when the job was started
- Clean history: Can review logs from multiple runs of the same job

Timestamp format: YYYYMMDD_HHMMSS (e.g., 20260109_220355)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-10 01:03:10 -05:00
f655c0199d fix(author): sanitize output filenames at job creation time
Added explicit sanitization of output paths when creating authoring jobs
to ensure special characters are removed before any filesystem operations.

The sanitization now happens in two places:
1. addAuthorToQueue: Sanitizes the output path for regular authoring jobs
2. addAuthorVideoTSToQueue: Sanitizes the output path for VIDEO_TS->ISO jobs

This ensures that:
- OutputFile field in Job struct never contains special characters
- Log filenames are sanitized (using the OutputFile basename)
- All filesystem operations use clean filenames

Before: /path/ifuckedmybestfriend'sgirlfriend.iso
After:  /path/ifuckedmybestfriendsgirlfriend.iso

The display title shown to users still contains the original text,
but all actual file operations use sanitized names.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-09 23:49:19 -05:00
df1f02b44a fix(author): sanitize log filenames to remove special characters
Applied sanitizeForPath to log filenames in createAuthorLog to ensure
log files don't contain special characters like apostrophes.

Before: ifuckedmybestfriend'sgirlfriend-author.videotools.log
After:  ifuckedmybestfriendsgirlfriend-author.videotools.log

This prevents filesystem issues and improves consistency with output
filenames.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-09 22:24:53 -05:00
fb90d17304 fix(author): sanitize special characters from output filenames
Extended sanitizeForPath to remove additional special characters:
- Apostrophes (')
- Double quotes (")
- Backticks (`)
- Exclamation marks (!)
- Question marks (?)
- Ampersands (&) → "and"

Before: ifuckedmybestfriend'sgirlfriend.iso
After:  ifuckedmybestfriendsgirlfriend.iso

This prevents filesystem issues and improves filename compatibility
across different operating systems and file sharing platforms.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-09 22:10:19 -05:00
8c0f182262 fix(player): calculate actual video frame number instead of display counter
The frame counter was showing the internal display loop counter (30fps)
instead of the actual video frame number. This caused the UI to show
frame numbers jumping by large increments (e.g., 260→304→348).

Fixed by calculating: actualFrameNumber = currentTime × fps

For a 60fps video:
- At 4.33s: frame 260 (not display counter 130)
- At 5.07s: frame 304 (not display counter 152)
- At 5.80s: frame 348 (not display counter 174)

Now the frame counter accurately reflects the actual video frame number,
progressing smoothly: 0, 1, 2, 3... (every 1/60th second for 60fps video).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-09 22:06:40 -05:00
7f8f045680 refactor(player): remove legacy UnifiedPlayer, GStreamer now mandatory
- Removed unified_ffmpeg_player.go and unified_player_adapter.go
- Updated frame_player_gstreamer.go to remove UnifiedPlayer fallback
- Updated frame_player_default.go to return error when GStreamer unavailable
- Updated PROJECT_STATUS.md: Player module now fully implemented with GStreamer
- Removed critical issues note about Player A/V sync problems

GStreamer is now the sole playback backend, providing stable A/V
synchronization and frame-accurate seeking. The broken FFmpeg pipe-based
UnifiedPlayer has been completely removed.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-09 22:02:22 -05:00
48eaf0be8d perf(player): optimize frame display loop for smooth playback
Performance improvements to eliminate choppy playback:

1. Cap display FPS at 30fps:
   - Even 60fps videos display at max 30fps
   - Reduces UI update overhead significantly
   - Human eye can't distinguish >30fps in preview player
   - Video plays at full 60fps internally, display throttled

2. Skip duplicate frames:
   - Track lastFrameTime from GStreamer
   - Only update UI when currentTime changes
   - Prevents refreshing same frame multiple times
   - Eliminates the "Frame 389 updated 17 times" issue

3. Remove verbose frame logging:
   - Removed per-frame debug log (was slowing down UI)
   - Keep INFO logs for start/stop events
   - Still log errors when they occur

4. Cleaner logging:
   - Show both video fps and display fps at startup
   - Makes performance characteristics visible

Results:
- Before: Choppy playback, same frame updated repeatedly
- After: Smooth 30fps display, no duplicate updates
- 4K video (3840x2160) now plays smoothly

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-09 21:54:55 -05:00
4c866e2aef feat(scripts): add run-debug.sh for convenient debug mode launching
Add convenience script for running VideoTools in debug mode:
- Automatically enables --debug flag
- Outputs to both console and timestamped log file
- Creates logs/ directory with organized debug logs
- Shows log file location before and after run

Usage:
  ./scripts/run-debug.sh

Logs saved to:
  logs/videotools_debug_YYYYMMDD_HHMMSS.log

This makes debugging much easier without typing the full command
with tee redirection every time.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-09 21:36:36 -05:00
6f8b04405a fix(player): clear canvas.Image File field to display GStreamer frames
Critical fix for Fyne canvas rendering:

Fyne's canvas.Image has two ways to display content:
1. File field (string path) - takes precedence
2. Image field (image.Image) - used if File is empty

When buildVideoPane creates canvas.Image with NewImageFromFile(),
it sets the File field. Later when frameDisplayLoop sets Image field,
Fyne still tries to render from File path, ignoring the Image data.

Fix: Clear img.File before setting img.Image in frameDisplayLoop.

This allows GStreamer frames to actually display on screen instead
of showing the static placeholder frame.

Without this fix:
- GStreamer extracts frames perfectly (confirmed by logs)
- Frames are set to img.Image and Refresh() is called
- But Fyne still renders the static file, not the dynamic frames

With this fix:
- img.File cleared, so Fyne uses img.Image field
- Dynamic GStreamer frames display in real-time
- Video playback now visible!

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-09 21:26:21 -05:00
9c801e4910 fix(player): ensure GStreamer produces and displays frames properly
Critical fixes for GStreamer playback:

1. Add preroll waiting in GStreamer.Load():
   - Wait for ASYNC_DONE message after setting to PAUSED
   - Ensures first frame is ready before playback
   - Prevents black screen on load

2. Fix frameDisplayLoop to always pull frames:
   - Remove paused check that blocked frame extraction
   - Frames now pulled even when paused (enables scrubbing)
   - Only update progress bar when playing

3. Add comprehensive logging:
   - Log each frame update with size and timestamp
   - Debug frame pull errors
   - Track paused state during updates

4. Fix initial paused state:
   - Explicitly set paused=true after load
   - Matches GStreamer's PAUSED state

These changes fix:
- Black screen issue (no frames displaying)
- Scrubbing not working (frames not available when paused)
- Fast duration counting (progress only updates when playing)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-09 19:22:08 -05:00
ca48de7dc6 feat(player): replace UnifiedPlayerAdapter with GStreamer in playSession
- Replace unifiedAdapter with gstPlayer in playSession struct
- Update all playSession methods to use GStreamerPlayer:
  - Play(), Pause(), Seek(), StepFrame()
  - SetVolume(), Stop(), stopLocked(), startLocked()
  - GetCurrentFrame()
- Add frameDisplayLoop() to continuously pull frames from GStreamer
- Connect GStreamer frame output to Fyne canvas for display
- Remove all UnifiedPlayerAdapter dependencies

This completes the GStreamer integration for both Player module
and Convert preview system. Both now use the same stable GStreamer
backend for video playback.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-09 03:50:32 -05:00
501e2622dc feat(player): integrate GStreamer for stable video playback
- Add GStreamer as mandatory core dependency in install.sh
- Create controller_gstreamer.go wrapping GStreamerPlayer
- Add missing methods to GStreamerPlayer (SetWindow, Stop, SetFullScreen)
- Fix GstSeekFlags type casting issue
- Update build scripts to always use -tags gstreamer
- Update controller_linux.go build tag to exclude when gstreamer enabled
- Add comprehensive migration documentation

GStreamer replaces the broken FFmpeg pipe-based UnifiedPlayer.
GStreamer 1.26+ provides frame-accurate seeking and reliable A/V sync.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-09 03:43:34 -05:00
3ff907cbbe Tighten author menu logo preview handling 2026-01-08 21:43:13 -05:00
68df790d27 Fix player frame generation and video playback
Major improvements to UnifiedPlayer:

1. GetFrameImage() now works when paused for responsive UI updates
2. Play() method properly starts FFmpeg process
3. Frame display loop runs continuously for smooth video display
4. Disabled audio temporarily to fix video playback fundamentals
5. Simplified FFmpeg command to focus on video stream only

Player now:
- Generates video frames correctly
- Shows video when paused
- Has responsive progress tracking
- Starts playback properly

Next steps: Re-enable audio playback once video is stable
2026-01-07 22:20:00 -05:00
00d462cca6 Revert "Remove unused settings module import"
This reverts commit 237075c12b.
2026-01-07 21:57:12 -05:00
237075c12b Remove unused settings module import 2026-01-07 21:56:09 -05:00
205724d7c1 Add timeout and no-stdin for thumbnail jobs 2026-01-07 16:04:40 -05:00
38c0d3e62f Enforce display aspect ratio in conversions 2026-01-07 15:38:34 -05:00
672a67fe2a Add horizontal padding to snippet controls 2026-01-07 15:31:05 -05:00
52beb6dac8 Restore UI noise opacity to 3 percent 2026-01-07 15:29:56 -05:00
39c5cc5c7d Harden Windows icon resource generation 2026-01-07 15:29:02 -05:00
db381b92df Enforce minimum window size via content guard 2026-01-07 15:19:43 -05:00
8a12f69169 Stabilize window minimums and player base size 2026-01-07 15:17:20 -05:00
fd1feb1641 Lower UI noise opacity 2026-01-07 15:11:22 -05:00
11cd7dc885 Add static UI noise overlay 2026-01-07 15:02:28 -05:00
bf2ec70ffe Embed Windows app icon via windres 2026-01-07 14:40:53 -05:00
3bf786533a Refine Upscale layout and settings panel 2026-01-07 14:34:10 -05:00
a04709593b Keep player size stable without video 2026-01-07 14:12:02 -05:00
d4f75832e4 Clamp player layout aspect and size 2026-01-07 13:51:29 -05:00
a8d2096686 Reduce default UI text sizes 2026-01-07 13:49:13 -05:00
4f855dbfe3 Stabilize convert left column layout 2026-01-07 13:47:53 -05:00
fab575cfab Add build time to scripts 2026-01-07 13:45:58 -05:00