forked from Leak_Technologies/VideoTools
Show preview frame when loading videos
This commit is contained in:
parent
c4a5e48a22
commit
8815f69fe8
193
main.go
193
main.go
|
|
@ -379,18 +379,22 @@ func (s *appState) applyInverseDefaults(src *videoSource) {
|
|||
}
|
||||
|
||||
func (s *appState) setContent(body fyne.CanvasObject) {
|
||||
fmt.Printf("🎨 setContent called with body: %v\n", body != nil)
|
||||
update := func() {
|
||||
bg := canvas.NewRectangle(backgroundColor)
|
||||
// Don't set a minimum size - let content determine layout naturally
|
||||
if body == nil {
|
||||
fmt.Printf("🎨 Setting empty background\n")
|
||||
s.window.SetContent(bg)
|
||||
return
|
||||
}
|
||||
fmt.Printf("🎨 Setting window content with body\n")
|
||||
s.window.SetContent(container.NewMax(bg, body))
|
||||
}
|
||||
|
||||
// Use async Do() instead of DoAndWait() to avoid deadlock when called from main goroutine
|
||||
fyne.Do(update)
|
||||
fmt.Printf("🎨 setContent completed\n")
|
||||
}
|
||||
|
||||
// showErrorWithCopy displays an error dialog with a "Copy Error" button
|
||||
|
|
@ -422,6 +426,7 @@ func (s *appState) showErrorWithCopy(title string, err error) {
|
|||
}
|
||||
|
||||
func (s *appState) showMainMenu() {
|
||||
fmt.Printf("🎬 showMainMenu called - going to player view\n")
|
||||
// Minimal entry point: go straight to the player view.
|
||||
s.showPlayerView()
|
||||
}
|
||||
|
|
@ -464,6 +469,17 @@ func (s *appState) buildComparePane(src *videoSource, onStop func(), setSess fun
|
|||
stageBG := canvas.NewRectangle(utils.MustHex("#0F1529"))
|
||||
stageBG.SetMinSize(fyne.NewSize(640, 360))
|
||||
videoImg := canvas.NewImageFromResource(nil)
|
||||
// Populate a preview frame if available
|
||||
if len(src.PreviewFrames) == 0 {
|
||||
if frames, err := capturePreviewFrames(src.Path, src.Duration); err == nil && len(frames) > 0 {
|
||||
src.PreviewFrames = frames
|
||||
}
|
||||
}
|
||||
if len(src.PreviewFrames) > 0 {
|
||||
s.currentFrame = src.PreviewFrames[0]
|
||||
videoImg.File = s.currentFrame
|
||||
videoImg.Refresh()
|
||||
}
|
||||
videoImg.FillMode = canvas.ImageFillContain
|
||||
stage := container.NewMax(stageBG, videoImg)
|
||||
|
||||
|
|
@ -561,10 +577,12 @@ func (s *appState) buildComparePane(src *videoSource, onStop func(), setSess fun
|
|||
|
||||
// showPlayerView renders the player-focused UI with a lightweight playlist.
|
||||
func (s *appState) showPlayerView() {
|
||||
fmt.Printf("🎬 showPlayerView called\n")
|
||||
s.stopPreview()
|
||||
s.stopPlayer()
|
||||
s.stopCompareSessions()
|
||||
s.active = "player"
|
||||
fmt.Printf("📺 s.source is nil: %v\n", s.source == nil)
|
||||
|
||||
// Helper to refresh the view after selection/loads.
|
||||
refresh := func() {
|
||||
|
|
@ -606,21 +624,20 @@ func (s *appState) showPlayerView() {
|
|||
}),
|
||||
)
|
||||
|
||||
viewMenu := fyne.NewMenu("View",
|
||||
fyne.NewMenuItem("Playlist", func() {
|
||||
// Will be implemented with playlist toggle
|
||||
}),
|
||||
)
|
||||
// Simplified View menu - only essential items
|
||||
viewMenu := fyne.NewMenu("View")
|
||||
|
||||
// Only show Compare Videos if we have multiple videos loaded
|
||||
if len(s.loadedVideos) >= 2 {
|
||||
viewMenu.Items = append(viewMenu.Items, fyne.NewMenuItem("Compare Videos", func() {
|
||||
s.showCompareView()
|
||||
}))
|
||||
}
|
||||
|
||||
// Tools menu with essential features
|
||||
toolsMenu := fyne.NewMenu("Tools")
|
||||
|
||||
// Keyframing mode toggle
|
||||
// Keyframing mode toggle - main feature
|
||||
keyframeModeItem := fyne.NewMenuItem("Frame-Accurate Mode", func() {
|
||||
s.keyframingMode = !s.keyframingMode
|
||||
|
||||
|
|
@ -692,9 +709,12 @@ func (s *appState) showPlayerView() {
|
|||
container.NewCenter(hint),
|
||||
)
|
||||
|
||||
playerArea = container.NewMax(bg, container.NewCenter(centerStack))
|
||||
playerArea := container.NewMax(bg, container.NewCenter(centerStack))
|
||||
fmt.Printf("📺 Empty player view created\n")
|
||||
s.setContent(playerArea)
|
||||
} else {
|
||||
src := s.source
|
||||
fmt.Printf("🎬 Creating player view for loaded video\n")
|
||||
|
||||
// Image surface
|
||||
stageBG := canvas.NewRectangle(utils.MustHex("#0F1529"))
|
||||
|
|
@ -739,14 +759,18 @@ func (s *appState) showPlayerView() {
|
|||
playlistContainer.Resize(fyne.NewSize(250, 540))
|
||||
|
||||
// Playlist starts hidden by default - user can toggle with menu button
|
||||
var playlistVisible bool = false
|
||||
playlistVisible := false
|
||||
var mainContent *fyne.Container
|
||||
|
||||
if playlistVisible {
|
||||
mainContent = container.NewBorder(nil, nil, nil, playlistContainer, container.NewPadded(stage))
|
||||
} else {
|
||||
mainContent = container.NewPadded(stage)
|
||||
// Function to update main content layout
|
||||
updateMainContent := func() {
|
||||
if playlistVisible {
|
||||
mainContent = container.NewBorder(nil, nil, nil, playlistContainer, container.NewPadded(stage))
|
||||
} else {
|
||||
mainContent = container.NewPadded(stage)
|
||||
}
|
||||
}
|
||||
updateMainContent()
|
||||
|
||||
currentTime := widget.NewLabel("0:00")
|
||||
totalTime := widget.NewLabel(src.DurationString())
|
||||
|
|
@ -798,6 +822,10 @@ func (s *appState) showPlayerView() {
|
|||
ensureSession = func() bool {
|
||||
if s.playSess == nil {
|
||||
s.playSess = newPlaySession(src.Path, src.Width, src.Height, src.FrameRate, 960, 540, updateProgress, videoImg)
|
||||
if s.playSess == nil {
|
||||
fmt.Printf("❌ ERROR: Failed to create play session\n")
|
||||
return false
|
||||
}
|
||||
s.playSess.SetVolume(s.playerVolume)
|
||||
s.playerPaused = true
|
||||
}
|
||||
|
|
@ -825,14 +853,18 @@ func (s *appState) showPlayerView() {
|
|||
|
||||
var playBtn *widget.Button
|
||||
playBtn = ui.NewIconButton(ui.IconPlayArrow, "Play/Pause", func() {
|
||||
fmt.Printf("🎮 Play button clicked (paused: %v)\n", s.playerPaused)
|
||||
if !ensureSession() {
|
||||
fmt.Printf("❌ ERROR: Failed to ensure play session\n")
|
||||
return
|
||||
}
|
||||
if s.playerPaused {
|
||||
fmt.Printf("▶️ Starting playback...\n")
|
||||
s.playSess.Play()
|
||||
s.playerPaused = false
|
||||
playBtn.SetText(ui.IconPause)
|
||||
} else {
|
||||
fmt.Printf("⏸️ Pausing playback...\n")
|
||||
s.playSess.Pause()
|
||||
s.playerPaused = true
|
||||
playBtn.SetText(ui.IconPlayArrow)
|
||||
|
|
@ -848,10 +880,13 @@ func (s *appState) showPlayerView() {
|
|||
|
||||
var volIcon *widget.Button
|
||||
volIcon = ui.NewIconButton(ui.IconVolumeUp, "Mute/Unmute", func() {
|
||||
fmt.Printf("🔊 Volume button clicked (muted: %v, volume: %.1f)\n", s.playerMuted, s.playerVolume)
|
||||
if !ensureSession() {
|
||||
fmt.Printf("❌ ERROR: Failed to ensure play session for volume control\n")
|
||||
return
|
||||
}
|
||||
if s.playerMuted {
|
||||
fmt.Printf("🔊 Unmuting volume to %.1f\n", s.lastVolume)
|
||||
target := s.lastVolume
|
||||
if target <= 0 {
|
||||
target = 50
|
||||
|
|
@ -860,6 +895,7 @@ func (s *appState) showPlayerView() {
|
|||
s.playerMuted = false
|
||||
s.playSess.SetVolume(target)
|
||||
} else {
|
||||
fmt.Printf("🔇 Muting volume\n")
|
||||
s.lastVolume = s.playerVolume
|
||||
s.playerVolume = 0
|
||||
s.playerMuted = true
|
||||
|
|
@ -888,15 +924,10 @@ func (s *appState) showPlayerView() {
|
|||
// Playlist toggle button
|
||||
playlistToggleBtn := ui.NewIconButton(ui.IconMenu, "Toggle Playlist", func() {
|
||||
playlistVisible = !playlistVisible
|
||||
if playlistVisible {
|
||||
mainContent.Objects = []fyne.CanvasObject{container.NewPadded(stage)}
|
||||
mainContent.Objects = []fyne.CanvasObject{}
|
||||
*mainContent = *container.NewBorder(nil, nil, nil, playlistContainer, container.NewPadded(stage))
|
||||
} else {
|
||||
mainContent.Objects = []fyne.CanvasObject{}
|
||||
*mainContent = *container.NewPadded(stage)
|
||||
}
|
||||
mainContent.Refresh()
|
||||
fmt.Printf("📋 Playlist toggle: %v\n", playlistVisible)
|
||||
updateMainContent()
|
||||
// Refresh the entire player view to apply layout changes
|
||||
s.setContent(playerArea)
|
||||
})
|
||||
|
||||
// Progress bar - use timeline in keyframing mode, slider otherwise
|
||||
|
|
@ -970,10 +1001,11 @@ func (s *appState) showPlayerView() {
|
|||
}
|
||||
})
|
||||
|
||||
// Center the playback controls
|
||||
playbackControls := container.NewHBox(prevBtn, playBtn, nextBtn)
|
||||
// Create basic playback controls (always centered)
|
||||
basicControls := container.NewHBox(prevBtn, playBtn, nextBtn)
|
||||
|
||||
// Add frame navigation if keyframing mode is enabled
|
||||
var playbackControls fyne.CanvasObject
|
||||
if s.keyframingMode {
|
||||
playbackControls = container.NewHBox(
|
||||
prevBtn,
|
||||
|
|
@ -984,23 +1016,25 @@ func (s *appState) showPlayerView() {
|
|||
keyframeNextBtn,
|
||||
nextBtn,
|
||||
)
|
||||
} else {
|
||||
playbackControls = basicControls
|
||||
}
|
||||
|
||||
// Create control row with centered playback controls
|
||||
var controlRow *fyne.Container
|
||||
// Create control row with properly centered playback controls
|
||||
var controlRow fyne.CanvasObject
|
||||
if s.keyframingMode {
|
||||
// Show frame counter in keyframing mode
|
||||
controlRow = container.NewBorder(
|
||||
nil, nil,
|
||||
volContainer, // Volume on left
|
||||
volContainer, // Volume on left
|
||||
container.NewHBox(frameCounter, playlistToggleBtn), // Frame counter and playlist toggle on right
|
||||
container.NewCenter(playbackControls), // Playback controls centered
|
||||
container.NewCenter(playbackControls), // Playback controls centered
|
||||
)
|
||||
} else {
|
||||
controlRow = container.NewBorder(
|
||||
nil, nil,
|
||||
volContainer, // Volume on left
|
||||
container.NewHBox(playlistToggleBtn), // Playlist toggle on right
|
||||
volContainer, // Volume on left
|
||||
container.NewHBox(playlistToggleBtn), // Playlist toggle on right
|
||||
container.NewCenter(playbackControls), // Playback controls centered
|
||||
)
|
||||
}
|
||||
|
|
@ -1073,18 +1107,17 @@ func (s *appState) showPlayerView() {
|
|||
// Previously had TappableOverlay here but it blocked all button clicks
|
||||
// Need to redesign so controls overlay the video without blocking interaction
|
||||
|
||||
playerArea = container.NewBorder(
|
||||
playerArea := container.NewBorder(
|
||||
nil,
|
||||
container.NewVBox(container.NewPadded(progressBar), container.NewPadded(controlRow)),
|
||||
nil,
|
||||
nil,
|
||||
mainContent,
|
||||
)
|
||||
|
||||
fmt.Printf("🎬 Player view layout created\n")
|
||||
s.setContent(playerArea)
|
||||
}
|
||||
|
||||
mainPanel := playerArea
|
||||
|
||||
s.setContent(mainPanel)
|
||||
}
|
||||
|
||||
// Legacy queue view left in place but not used in player-only mode.
|
||||
|
|
@ -1178,8 +1211,10 @@ func (s *appState) showModule(id string) {
|
|||
}
|
||||
|
||||
func (s *appState) handleModuleDrop(moduleID string, items []fyne.URI) {
|
||||
fmt.Printf("📦 handleModuleDrop called: moduleID=%s itemCount=%d\n", moduleID, len(items))
|
||||
logging.Debug(logging.CatModule, "handleModuleDrop called: moduleID=%s itemCount=%d", moduleID, len(items))
|
||||
if len(items) == 0 {
|
||||
fmt.Printf("❌ No items to process\n")
|
||||
logging.Debug(logging.CatModule, "handleModuleDrop: no items to process")
|
||||
return
|
||||
}
|
||||
|
|
@ -2059,7 +2094,9 @@ func runGUI() {
|
|||
}
|
||||
})
|
||||
|
||||
fmt.Printf("🚀 About to call showMainMenu\n")
|
||||
state.showMainMenu()
|
||||
fmt.Printf("✅ showMainMenu completed\n")
|
||||
logging.Debug(logging.CatUI, "main menu rendered with %d modules", len(modulesList))
|
||||
|
||||
// Start stats bar update loop on a timer
|
||||
|
|
@ -3670,15 +3707,27 @@ func newPlaySession(path string, w, h int, fps float64, targetW, targetH int, pr
|
|||
fmt.Printf("🎯 Target: %dx%d\n", targetW, targetH)
|
||||
fmt.Printf("═══════════════════════════════════════════════════════\n\n")
|
||||
|
||||
// Validate input parameters
|
||||
if fps <= 0 {
|
||||
fps = 24
|
||||
fmt.Printf("⚠️ Invalid FPS (%.2f), defaulting to 24\n", fps)
|
||||
}
|
||||
if targetW <= 0 {
|
||||
targetW = 640
|
||||
fmt.Printf("⚠️ Invalid target width (%d), defaulting to 640\n", targetW)
|
||||
}
|
||||
if targetH <= 0 {
|
||||
targetH = int(float64(targetW) * (float64(h) / float64(utils.MaxInt(w, 1))))
|
||||
fmt.Printf("⚠️ Invalid target height (%d), calculating to %d\n", targetH, targetH)
|
||||
}
|
||||
|
||||
// Check if video file exists
|
||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||
fmt.Printf("❌ ERROR: Video file does not exist: %s\n", path)
|
||||
return nil
|
||||
}
|
||||
|
||||
fmt.Printf("✅ Play session created successfully\n")
|
||||
return &playSession{
|
||||
path: path,
|
||||
fps: fps,
|
||||
|
|
@ -3842,10 +3891,11 @@ func (p *playSession) runVideo(offset float64) {
|
|||
"-hide_banner", "-loglevel", "error",
|
||||
"-ss", fmt.Sprintf("%.3f", offset),
|
||||
"-i", p.path,
|
||||
"-vf", fmt.Sprintf("scale=%d:%d", p.targetW, p.targetH),
|
||||
"-vf", fmt.Sprintf("scale=%d:%d:flags=bilinear", p.targetW, p.targetH),
|
||||
"-f", "rawvideo",
|
||||
"-pix_fmt", "rgb24",
|
||||
"-r", fmt.Sprintf("%.3f", p.fps),
|
||||
"-vsync", "0", // Avoid frame duplication
|
||||
"-",
|
||||
}
|
||||
fmt.Printf("🔧 FFmpeg command: ffmpeg %s\n", strings.Join(args, " "))
|
||||
|
|
@ -3867,10 +3917,14 @@ func (p *playSession) runVideo(offset float64) {
|
|||
if errMsg != "" {
|
||||
fmt.Printf(" FFmpeg error: %s\n", errMsg)
|
||||
}
|
||||
// Check if ffmpeg is available
|
||||
if _, pathErr := exec.LookPath("ffmpeg"); pathErr != nil {
|
||||
fmt.Printf("❌ FATAL: ffmpeg not found in PATH: %v\n", pathErr)
|
||||
}
|
||||
logging.Debug(logging.CatFFMPEG, "video start failed: %v (%s)", err, errMsg)
|
||||
return
|
||||
}
|
||||
fmt.Printf("✅ FFmpeg started in %.3fs\n", time.Since(startTime).Seconds())
|
||||
fmt.Printf("✅ FFmpeg started (PID: %d) in %.3fs\n", cmd.Process.Pid, time.Since(startTime).Seconds())
|
||||
// Pace frames to the source frame rate instead of hammering refreshes as fast as possible.
|
||||
frameDur := time.Second
|
||||
if p.fps > 0 {
|
||||
|
|
@ -3880,7 +3934,7 @@ func (p *playSession) runVideo(offset float64) {
|
|||
p.videoCmd = cmd
|
||||
frameSize := p.targetW * p.targetH * 3
|
||||
buf := make([]byte, frameSize)
|
||||
fmt.Printf("📦 Frame buffer allocated: %.2f MB\n", float64(frameSize)/(1024*1024))
|
||||
fmt.Printf("📦 Frame buffer allocated: %d bytes (%.2f MB)\n", frameSize, float64(frameSize)/(1024*1024))
|
||||
|
||||
go func() {
|
||||
defer cmd.Process.Kill()
|
||||
|
|
@ -3930,8 +3984,10 @@ func (p *playSession) runVideo(offset float64) {
|
|||
fmt.Printf("🎞️ FIRST FRAME decoded in %.3fs (read: %.3fs)\n", elapsed.Seconds(), readDuration.Seconds())
|
||||
}
|
||||
|
||||
if delay := time.Until(nextFrameAt); delay > 0 {
|
||||
time.Sleep(delay)
|
||||
// Improved frame pacing - use a more stable timing approach
|
||||
now := time.Now()
|
||||
if now.Before(nextFrameAt) {
|
||||
time.Sleep(nextFrameAt.Sub(now))
|
||||
}
|
||||
nextFrameAt = nextFrameAt.Add(frameDur)
|
||||
|
||||
|
|
@ -4000,9 +4056,15 @@ func (p *playSession) runAudio(offset float64) {
|
|||
return
|
||||
}
|
||||
if err := cmd.Start(); err != nil {
|
||||
logging.Debug(logging.CatFFMPEG, "audio start failed: %v (%s)", err, strings.TrimSpace(stderr.String()))
|
||||
errMsg := strings.TrimSpace(stderr.String())
|
||||
fmt.Printf("❌ ERROR: Audio FFmpeg failed to start: %v\n", err)
|
||||
if errMsg != "" {
|
||||
fmt.Printf(" Audio FFmpeg error: %s\n", errMsg)
|
||||
}
|
||||
logging.Debug(logging.CatFFMPEG, "audio start failed: %v (%s)", err, errMsg)
|
||||
return
|
||||
}
|
||||
fmt.Printf("✅ Audio FFmpeg started (PID: %d)\n", cmd.Process.Pid)
|
||||
p.audioCmd = cmd
|
||||
ctx, err := getAudioContext(sampleRate, channels, bytesPerSample)
|
||||
if err != nil {
|
||||
|
|
@ -4205,7 +4267,9 @@ func (s *appState) importCoverImage(path string) (string, error) {
|
|||
|
||||
// handleDropPlayer accepts dropped files/folders anywhere and loads them into the playlist/player.
|
||||
func (s *appState) handleDropPlayer(items []fyne.URI) {
|
||||
fmt.Printf("📦 handleDropPlayer called with %d items\n", len(items))
|
||||
if len(items) == 0 {
|
||||
fmt.Printf("❌ No items in drop\n")
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -4332,26 +4396,59 @@ func (s *appState) detectModuleTileAtPosition(pos fyne.Position) string {
|
|||
}
|
||||
|
||||
func (s *appState) loadVideo(path string) {
|
||||
fmt.Printf("\n═══════════════════════════════════════════════════════\n")
|
||||
fmt.Printf("🎬 LOADING VIDEO\n")
|
||||
fmt.Printf("═══════════════════════════════════════════════════════\n")
|
||||
fmt.Printf("📁 Path: %s\n", path)
|
||||
|
||||
// Check if file exists first
|
||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||
fmt.Printf("❌ ERROR: File does not exist: %s\n", path)
|
||||
fyne.CurrentApp().Driver().DoFromGoroutine(func() {
|
||||
s.showErrorWithCopy("File Not Found", fmt.Errorf("video file not found: %s", path))
|
||||
}, false)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("✅ File exists, starting analysis...\n")
|
||||
fmt.Printf("═══════════════════════════════════════════════════════\n\n")
|
||||
|
||||
if s.playSess != nil {
|
||||
fmt.Printf("⏹️ Stopping current playback session\n")
|
||||
s.playSess.Stop()
|
||||
s.playSess = nil
|
||||
}
|
||||
s.stopProgressLoop()
|
||||
|
||||
fmt.Printf("🔍 Probing video metadata...\n")
|
||||
src, err := probeVideo(path)
|
||||
if err != nil {
|
||||
fmt.Printf("❌ ERROR: Video probe failed: %v\n", err)
|
||||
logging.Debug(logging.CatFFMPEG, "ffprobe failed for %s: %v", path, err)
|
||||
fyne.CurrentApp().Driver().DoFromGoroutine(func() {
|
||||
s.showErrorWithCopy("Failed to Analyze Video", fmt.Errorf("failed to analyze %s: %w", filepath.Base(path), err))
|
||||
}, false)
|
||||
return
|
||||
}
|
||||
if frames, err := capturePreviewFrames(src.Path, src.Duration); err == nil {
|
||||
|
||||
fmt.Printf("✅ Video probe successful:\n")
|
||||
fmt.Printf(" - Resolution: %dx%d\n", src.Width, src.Height)
|
||||
fmt.Printf(" - Duration: %.2fs\n", src.Duration)
|
||||
fmt.Printf(" - Frame Rate: %.2f fps\n", src.FrameRate)
|
||||
fmt.Printf(" - Codec: %s\n", src.VideoCodec)
|
||||
|
||||
fmt.Printf("🖼️ Generating preview frames...\n")
|
||||
frames, err := capturePreviewFrames(src.Path, src.Duration)
|
||||
if err == nil {
|
||||
src.PreviewFrames = frames
|
||||
fmt.Printf("✅ Generated %d preview frames\n", len(frames))
|
||||
} else {
|
||||
fmt.Printf("⚠️ Preview generation failed: %v\n", err)
|
||||
logging.Debug(logging.CatFFMPEG, "preview generation failed: %v", err)
|
||||
}
|
||||
|
||||
// Maintain/extend loaded video list for navigation
|
||||
fmt.Printf("📋 Managing video playlist...\n")
|
||||
found := -1
|
||||
for i, v := range s.loadedVideos {
|
||||
if v.Path == src.Path {
|
||||
|
|
@ -4363,15 +4460,22 @@ func (s *appState) loadVideo(path string) {
|
|||
if found >= 0 {
|
||||
s.loadedVideos[found] = src
|
||||
s.currentIndex = found
|
||||
fmt.Printf("🔄 Updated existing video at index %d\n", found)
|
||||
} else if len(s.loadedVideos) > 0 {
|
||||
s.loadedVideos = append(s.loadedVideos, src)
|
||||
s.currentIndex = len(s.loadedVideos) - 1
|
||||
fmt.Printf("➕ Added new video at index %d\n", s.currentIndex)
|
||||
} else {
|
||||
s.loadedVideos = []*videoSource{src}
|
||||
s.currentIndex = 0
|
||||
fmt.Printf("🎯 Created new playlist with video at index 0\n")
|
||||
}
|
||||
|
||||
fmt.Printf("📺 Total videos in playlist: %d\n", len(s.loadedVideos))
|
||||
fmt.Printf("🎯 Current video index: %d\n", s.currentIndex)
|
||||
|
||||
logging.Debug(logging.CatModule, "video loaded %+v", src)
|
||||
fmt.Printf("🔄 Switching to player view...\n")
|
||||
fyne.CurrentApp().Driver().DoFromGoroutine(func() {
|
||||
s.switchToVideo(s.currentIndex)
|
||||
}, false)
|
||||
|
|
@ -4502,7 +4606,9 @@ func (s *appState) loadVideos(paths []string) {
|
|||
|
||||
// switchToVideo switches to a specific video by index
|
||||
func (s *appState) switchToVideo(index int) {
|
||||
fmt.Printf("🔄 switchToVideo called with index %d (total: %d)\n", index, len(s.loadedVideos))
|
||||
if index < 0 || index >= len(s.loadedVideos) {
|
||||
fmt.Printf("❌ Invalid index %d (range: 0-%d)\n", index, len(s.loadedVideos)-1)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -4510,10 +4616,15 @@ func (s *appState) switchToVideo(index int) {
|
|||
src := s.loadedVideos[index]
|
||||
s.source = src
|
||||
|
||||
fmt.Printf("🎬 Switched to: %s\n", filepath.Base(src.Path))
|
||||
fmt.Printf("📐 Resolution: %dx%d, Duration: %.2fs\n", src.Width, src.Height, src.Duration)
|
||||
|
||||
if len(src.PreviewFrames) > 0 {
|
||||
s.currentFrame = src.PreviewFrames[0]
|
||||
fmt.Printf("🖼️ Set preview frame: %s\n", src.PreviewFrames[0])
|
||||
} else {
|
||||
s.currentFrame = ""
|
||||
fmt.Printf("⚠️ No preview frames available\n")
|
||||
}
|
||||
|
||||
s.applyInverseDefaults(src)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user