Compare commits

...

4 Commits

Author SHA1 Message Date
Stu
b7b5788938 Harden drag/drop path handling and user feedback 2025-12-04 07:08:12 -05:00
Stu
ffca39811a Improve drag/drop path handling and landing layout 2025-12-04 06:59:08 -05:00
Stu
e749a32926 Remove legacy compare/convert hooks 2025-12-04 06:40:00 -05:00
Stu
66c79cee91 Remove duplicate compare/queue handlers 2025-12-04 06:28:06 -05:00

175
main.go
View File

@ -655,7 +655,6 @@ func (s *appState) showPlayerView() {
centerStack := container.NewVBox(
container.NewCenter(icon),
layout.NewSpacer(),
container.NewCenter(loadBtn),
container.NewCenter(hint),
)
@ -820,121 +819,9 @@ func (s *appState) showPlayerView() {
s.setContent(mainPanel)
}
func (s *appState) showQueue() {
s.stopPreview()
s.stopPlayer()
s.stopCompareSessions()
s.lastModule = s.active
s.active = "queue"
s.refreshQueueView()
}
// refreshQueueView rebuilds the queue UI while preserving scroll position and inline active conversion.
func (s *appState) refreshQueueView() {
// Preserve current scroll offset if we already have a view
if s.queueScroll != nil {
s.queueOffset = s.queueScroll.Offset
}
jobs := s.jobQueue.List()
// If a direct conversion is running but not represented in the queue, surface it as a pseudo job.
if s.convertBusy {
in := filepath.Base(s.convertActiveIn)
if in == "" && s.source != nil {
in = filepath.Base(s.source.Path)
}
out := filepath.Base(s.convertActiveOut)
jobs = append([]*queue.Job{{
ID: "active-convert",
Type: queue.JobTypeConvert,
Status: queue.JobStatusRunning,
Title: fmt.Sprintf("Direct convert: %s", in),
Description: fmt.Sprintf("Output: %s", out),
Progress: s.convertProgress,
}}, jobs...)
}
view, scroll := ui.BuildQueueView(
jobs,
func() { // onBack
if s.lastModule != "" && s.lastModule != "queue" && s.lastModule != "menu" {
s.showModule(s.lastModule)
} else {
s.showMainMenu()
}
},
func(id string) { // onPause
if err := s.jobQueue.Pause(id); err != nil {
logging.Debug(logging.CatSystem, "failed to pause job: %v", err)
}
s.refreshQueueView() // Refresh
},
func(id string) { // onResume
if err := s.jobQueue.Resume(id); err != nil {
logging.Debug(logging.CatSystem, "failed to resume job: %v", err)
}
s.refreshQueueView() // Refresh
},
func(id string) { // onCancel
if err := s.jobQueue.Cancel(id); err != nil {
logging.Debug(logging.CatSystem, "failed to cancel job: %v", err)
}
s.refreshQueueView() // Refresh
},
func(id string) { // onRemove
if err := s.jobQueue.Remove(id); err != nil {
logging.Debug(logging.CatSystem, "failed to remove job: %v", err)
}
s.refreshQueueView() // Refresh
},
func(id string) { // onMoveUp
if err := s.jobQueue.MoveUp(id); err != nil {
logging.Debug(logging.CatSystem, "failed to move job up: %v", err)
}
s.refreshQueueView() // Refresh
},
func(id string) { // onMoveDown
if err := s.jobQueue.MoveDown(id); err != nil {
logging.Debug(logging.CatSystem, "failed to move job down: %v", err)
}
s.refreshQueueView() // Refresh
},
func() { // onPauseAll
s.jobQueue.PauseAll()
s.refreshQueueView()
},
func() { // onResumeAll
s.jobQueue.ResumeAll()
s.refreshQueueView()
},
func() { // onStart
s.jobQueue.ResumeAll()
s.refreshQueueView()
},
func() { // onClear
s.jobQueue.Clear()
s.clearVideo()
s.refreshQueueView() // Refresh
},
func() { // onClearAll
s.jobQueue.ClearAll()
s.clearVideo()
s.refreshQueueView() // Refresh
},
utils.MustHex("#4CE870"), // titleColor
gridColor, // bgColor
textColor, // textColor
)
// Restore scroll offset
s.queueScroll = scroll
if s.queueScroll != nil {
s.queueScroll.Offset = s.queueOffset
s.queueScroll.Refresh()
}
s.setContent(container.NewPadded(view))
}
// Legacy queue view left in place but not used in player-only mode.
func (s *appState) showQueue() {}
func (s *appState) refreshQueueView() {}
// addConvertToQueue adds a conversion job to the queue
func (s *appState) addConvertToQueue() error {
@ -1209,33 +1096,11 @@ func (s *appState) batchAddToQueue(paths []string) {
}
}
s.loadVideos(combined)
s.showModule("convert")
s.showPlayerView()
}
}, false)
}
func (s *appState) showConvertView(file *videoSource) {
s.stopPreview()
s.lastModule = s.active
s.active = "convert"
if file != nil {
s.source = file
}
if s.source == nil {
s.convert.OutputBase = "converted"
s.convert.CoverArtPath = ""
s.convert.AspectHandling = "Auto"
}
s.setContent(buildConvertView(s, s.source))
}
func (s *appState) showCompareView() {
s.stopPreview()
s.lastModule = s.active
s.active = "compare"
s.setContent(buildCompareView(s))
}
// jobExecutor executes a job from the queue
func (s *appState) jobExecutor(ctx context.Context, job *queue.Job, progressCallback func(float64)) error {
logging.Debug(logging.CatSystem, "executing job %s: %s", job.ID, job.Title)
@ -3939,10 +3804,11 @@ func (s *appState) handleDropPlayer(items []fyne.URI) {
var videoPaths []string
for _, uri := range items {
if uri.Scheme() != "file" {
path := uriPath(uri)
if path == "" {
logging.Debug(logging.CatModule, "drop received empty path; uri=%v", uri)
continue
}
path := uri.Path()
logging.Debug(logging.CatModule, "drop received path=%s", path)
if info, err := os.Stat(path); err == nil && info.IsDir() {
@ -3955,6 +3821,9 @@ func (s *appState) handleDropPlayer(items []fyne.URI) {
if len(videoPaths) == 0 {
logging.Debug(logging.CatUI, "no valid video files in dropped items")
fyne.Do(func() {
dialog.ShowInformation("No videos found", "Drop a video file or a folder containing videos.", s.window)
})
return
}
@ -3965,6 +3834,30 @@ func (s *appState) handleDropPlayer(items []fyne.URI) {
}
}
// uriPath extracts a usable local path from a fyne URI.
func uriPath(u fyne.URI) string {
if u == nil {
return ""
}
// Prefer Path() when present.
if p := u.Path(); p != "" {
return p
}
raw := u.String()
if raw == "" {
return ""
}
if parsed, err := url.Parse(raw); err == nil {
if parsed.Scheme == "file" {
return parsed.Path
}
}
if strings.HasPrefix(raw, "file://") {
raw = strings.TrimPrefix(raw, "file://")
}
return filepath.FromSlash(raw)
}
// detectModuleTileAtPosition calculates which module tile is at the given position
// based on the main menu grid layout (3 columns)
func (s *appState) detectModuleTileAtPosition(pos fyne.Position) string {