diff --git a/author_module.go b/author_module.go index 1181a9d..d58eb29 100644 --- a/author_module.go +++ b/author_module.go @@ -103,7 +103,7 @@ func (s *appState) applyAuthorConfig(cfg authorConfig) { s.authorCreateMenu = cfg.CreateMenu s.authorTreatAsChapters = cfg.TreatAsChapters s.authorSceneThreshold = cfg.SceneThreshold - s.authorMenuTemplate = cfg.MenuTemplate + // MenuTemplate field doesn't exist in authorConfig struct - remove this line } func (s *appState) persistAuthorConfig() { @@ -116,7 +116,6 @@ func (s *appState) persistAuthorConfig() { CreateMenu: s.authorCreateMenu, TreatAsChapters: s.authorTreatAsChapters, SceneThreshold: s.authorSceneThreshold, - MenuTemplate: s.authorMenuTemplate, } if err := savePersistedAuthorConfig(cfg); err != nil { logging.Debug(logging.CatSystem, "failed to persist author config: %v", err) @@ -822,7 +821,6 @@ func buildAuthorSettingsTab(state *appState) fyne.CanvasObject { CreateMenu: state.authorCreateMenu, TreatAsChapters: state.authorTreatAsChapters, SceneThreshold: state.authorSceneThreshold, - MenuTemplate: state.authorMenuTemplate, } if err := savePersistedAuthorConfig(cfg); err != nil { dialog.ShowError(fmt.Errorf("failed to save config: %w", err), state.window) @@ -1763,25 +1761,25 @@ func (s *appState) addAuthorToQueue(paths []string, region, aspect, title, outpu } config := map[string]interface{}{ - "paths": paths, - "region": region, - "aspect": aspect, - "title": title, - "outputPath": outputPath, - "makeISO": makeISO, - "treatAsChapters": s.authorTreatAsChapters, - "clips": clips, - "chapters": chapters, - "discSize": s.authorDiscSize, - "outputType": s.authorOutputType, - "authorTitle": s.authorTitle, - "authorRegion": s.authorRegion, - "authorAspect": s.authorAspectRatio, - "createMenu": s.authorCreateMenu, - "chapterSource": s.authorChapterSource, - "subtitleTracks": append([]string{}, s.authorSubtitles...), - "additionalAudios": append([]string{}, s.authorAudioTracks...), - "menuTemplate": s.authorMenuTemplate, + "paths": paths, + "region": region, + "aspect": aspect, + "title": title, + "outputPath": outputPath, + "makeISO": makeISO, + "treatAsChapters": s.authorTreatAsChapters, + "clips": clips, + "chapters": chapters, + "discSize": s.authorDiscSize, + "outputType": s.authorOutputType, + "authorTitle": s.authorTitle, + "authorRegion": s.authorRegion, + "authorAspect": s.authorAspectRatio, + "createMenu": s.authorCreateMenu, + "chapterSource": s.authorChapterSource, + "subtitleTracks": append([]string{}, s.authorSubtitles...), + "additionalAudios": append([]string{}, s.authorAudioTracks...), + "menuTemplate": s.authorMenuTemplate, "menuBackgroundImage": s.authorMenuBackgroundImage, } diff --git a/internal/ui/colors.go b/internal/ui/colors.go index dba4094..6fc7bc0 100644 --- a/internal/ui/colors.go +++ b/internal/ui/colors.go @@ -12,43 +12,43 @@ import ( // Container / Format Colors (File Wrapper) var ( - ColorMKV = utils.MustHex("#2563EB") // Deep Blue - Flexible container + ColorMKV = utils.MustHex("#3B82F6") // Blue - Flexible container ColorRemux = utils.MustHex("#9CA3AF") // Neutral Grey - Lossless remux - ColorMP4 = utils.MustHex("#1D4ED8") // Navy Blue - Consumer-friendly - ColorMOV = utils.MustHex("#6366F1") // Indigo - Pro / Apple lineage - ColorAVI = utils.MustHex("#64748B") // Slate - Legacy container - ColorWEBM = utils.MustHex("#059669") // Emerald - Web-native - ColorTS = utils.MustHex("#D97706") // Amber - Broadcast / transport streams - ColorM2TS = utils.MustHex("#EA580C") // Orange - Broadcast / transport streams + ColorMP4 = utils.MustHex("#22C55E") // Green - Consumer-friendly + ColorMOV = utils.MustHex("#A855F7") // Purple - Pro / Apple lineage + ColorAVI = utils.MustHex("#F97316") // Orange - Legacy container + ColorWEBM = utils.MustHex("#14B8A6") // Teal - Web-native + ColorTS = utils.MustHex("#F59E0B") // Amber - Broadcast / transport streams + ColorM2TS = utils.MustHex("#EAB308") // Yellow - Broadcast / transport streams ) // Video Codec Colors (Compression Method) // Modern / Efficient Codecs var ( - ColorAV1 = utils.MustHex("#22C55E") // Green - Modern, efficient - ColorHEVC = utils.MustHex("#0D9488") // Teal - Modern, efficient - ColorH265 = utils.MustHex("#0D9488") // Teal - Same as HEVC - ColorVP9 = utils.MustHex("#06B6D4") // Cyan - Modern, efficient + ColorAV1 = utils.MustHex("#F97316") // Orange - Modern, efficient + ColorHEVC = utils.MustHex("#22C55E") // Green - Modern, efficient + ColorH265 = utils.MustHex("#22C55E") // Green - Same as HEVC + ColorVP9 = utils.MustHex("#8B5CF6") // Violet - Modern, efficient ) // Established / Legacy Video Codecs var ( - ColorH264 = utils.MustHex("#38BDF8") // Sky - Compatibility - ColorAVC = utils.MustHex("#38BDF8") // Sky - Same as H.264 - ColorMPEG2 = utils.MustHex("#FBBF24") // Amber - Legacy / broadcast - ColorDivX = utils.MustHex("#FB7185") // Rose - Legacy - ColorXviD = utils.MustHex("#FB7185") // Rose - Legacy - ColorMPEG4 = utils.MustHex("#FB7185") // Rose - Legacy + ColorH264 = utils.MustHex("#3B82F6") // Blue - Compatibility + ColorAVC = utils.MustHex("#3B82F6") // Blue - Same as H.264 + ColorMPEG2 = utils.MustHex("#EAB308") // Yellow - Legacy / broadcast + ColorDivX = utils.MustHex("#EF4444") // Red - Legacy + ColorXviD = utils.MustHex("#EF4444") // Red - Legacy + ColorMPEG4 = utils.MustHex("#EF4444") // Red - Legacy ) // Audio Codec Colors (Secondary but Distinct) var ( - ColorOpus = utils.MustHex("#DB2777") // Magenta - Modern audio - ColorAAC = utils.MustHex("#FB7185") // Rose - Common audio - ColorFLAC = utils.MustHex("#C084FC") // Violet - Lossless audio + ColorOpus = utils.MustHex("#EC4899") // Pink - Modern audio + ColorAAC = utils.MustHex("#06B6D4") // Cyan - Common audio + ColorFLAC = utils.MustHex("#A855F7") // Purple - Lossless audio ColorMP3 = utils.MustHex("#EF4444") // Red - Legacy audio ColorAC3 = utils.MustHex("#F59E0B") // Amber - Surround audio - ColorVorbis = utils.MustHex("#F97316") // Orange - Open codec + ColorVorbis = utils.MustHex("#22C55E") // Green - Open codec ) // Pixel Format / Colour Data (Technical Metadata) @@ -73,8 +73,10 @@ func GetContainerColor(format string) color.Color { return ColorAVI case "webm": return ColorWEBM - case "ts", "m2ts", "mts": + case "ts": return ColorTS + case "m2ts", "mts": + return ColorM2TS default: return color.RGBA{100, 100, 100, 255} // Default grey } diff --git a/main.go b/main.go index 158a7f1..82131d6 100644 --- a/main.go +++ b/main.go @@ -10975,24 +10975,24 @@ func newPlaySession(path string, w, h int, fps, duration float64, targetW, targe if targetH <= 0 { targetH = int(float64(targetW) * (float64(h) / float64(utils.MaxInt(w, 1)))) } - + // Create UnifiedPlayer adapter for stable A/V playback unifiedAdapter := player.NewUnifiedPlayerAdapter(path, w, h, fps, duration, targetW, targetH, prog, frameFunc, img) - + return &playSession{ - path: path, - fps: fps, - width: w, - height: h, - targetW: targetW, - targetH: targetH, - volume: 100, - duration: duration, - stop: make(chan struct{}), - done: make(chan struct{}), - prog: prog, - frameFunc: frameFunc, - img: img, + path: path, + fps: fps, + width: w, + height: h, + targetW: targetW, + targetH: targetH, + volume: 100, + duration: duration, + stop: make(chan struct{}), + done: make(chan struct{}), + prog: prog, + frameFunc: frameFunc, + img: img, unifiedAdapter: unifiedAdapter, } } @@ -11000,14 +11000,14 @@ func newPlaySession(path string, w, h int, fps, duration float64, targetW, targe func (p *playSession) Play() { p.mu.Lock() defer p.mu.Unlock() - + // Use UnifiedPlayer adapter if available if p.unifiedAdapter != nil { p.unifiedAdapter.Play() p.paused = false return } - + // Fallback to dual-process if p.videoCmd == nil && p.audioCmd == nil { p.startLocked(p.current) @@ -11019,14 +11019,14 @@ func (p *playSession) Play() { func (p *playSession) Pause() { p.mu.Lock() defer p.mu.Unlock() - + // Use UnifiedPlayer adapter if available if p.unifiedAdapter != nil { p.unifiedAdapter.Pause() p.paused = true return } - + p.paused = true } @@ -11036,7 +11036,7 @@ func (p *playSession) Seek(offset float64) { if offset < 0 { offset = 0 } - + // Use UnifiedPlayer adapter if available if p.unifiedAdapter != nil { p.unifiedAdapter.Seek(offset) @@ -11044,7 +11044,7 @@ func (p *playSession) Seek(offset float64) { p.paused = p.unifiedAdapter.IsPlaying() == false return } - + // Fallback to dual-process paused := p.paused p.current = offset @@ -11076,7 +11076,7 @@ func (p *playSession) StepFrame(delta int) { // Use UnifiedPlayer adapter if available if p.unifiedAdapter != nil { p.unifiedAdapter.StepFrame(delta) - p.current = p.unifiedAdapter.GetCurrentFrame() / p.fps + p.current = float64(p.unifiedAdapter.GetCurrentFrame()) / p.fps p.paused = true return } @@ -11129,12 +11129,12 @@ func (p *playSession) StepFrame(delta int) { func (p *playSession) GetCurrentFrame() int { p.mu.Lock() defer p.mu.Unlock() - + // Use UnifiedPlayer adapter if available if p.unifiedAdapter != nil { return p.unifiedAdapter.GetCurrentFrame() } - + return p.frameN } @@ -11142,13 +11142,13 @@ func (p *playSession) SetVolume(v float64) { p.mu.Lock() defer p.mu.Unlock() p.volume = v - + // Use UnifiedPlayer adapter if available if p.unifiedAdapter != nil { p.unifiedAdapter.SetVolume(v) return } - + // Fallback to dual-process if p.audioCmd != nil && p.audioCmd.Process != nil { // Send volume command to FFmpeg @@ -11157,6 +11157,13 @@ func (p *playSession) SetVolume(v float64) { } } +// writeStringToStdin writes a command to the stdin of audio FFmpeg process +func (p *playSession) writeStringToStdin(cmd string) { + // This would require setting up stdin pipe in audio cmd creation + // For now, just log the command as dual-process audio is deprecated + logging.Debug(logging.CatFFMPEG, "writeStringToStdin called with: %s", cmd) +} + func (p *playSession) restartAudio(offset float64) { p.mu.Lock() defer p.mu.Unlock() @@ -11173,13 +11180,13 @@ func (p *playSession) restartAudio(offset float64) { func (p *playSession) Stop() { p.mu.Lock() defer p.mu.Unlock() - + // Use UnifiedPlayer adapter if available if p.unifiedAdapter != nil { p.unifiedAdapter.Stop() return } - + // Fallback to dual-process p.stopLocked() } @@ -11190,7 +11197,7 @@ func (p *playSession) stopLocked() { p.unifiedAdapter.Stop() return } - + // Fallback to dual-process cleanup select { case <-p.stop: @@ -11220,14 +11227,14 @@ func (p *playSession) startLocked(offset float64) { p.videoTime = offset p.syncOffset = 0 logging.Debug(logging.CatFFMPEG, "playSession start path=%s offset=%.3f fps=%.3f target=%dx%d", p.path, offset, p.fps, p.targetW, p.targetH) - + // If using UnifiedPlayer adapter, no need to run dual-process if p.unifiedAdapter != nil { // UnifiedPlayer handles A/V sync internally p.unifiedAdapter.Seek(offset) return } - + // Fallback to dual-process (old method) p.runVideo(offset) p.runAudio(offset)