Increase color separation for formats and codecs

This commit is contained in:
Stu Leak 2026-01-06 18:48:57 -05:00
parent 6c26855c4b
commit b7afb3a48e
3 changed files with 82 additions and 75 deletions

View File

@ -103,7 +103,7 @@ func (s *appState) applyAuthorConfig(cfg authorConfig) {
s.authorCreateMenu = cfg.CreateMenu s.authorCreateMenu = cfg.CreateMenu
s.authorTreatAsChapters = cfg.TreatAsChapters s.authorTreatAsChapters = cfg.TreatAsChapters
s.authorSceneThreshold = cfg.SceneThreshold s.authorSceneThreshold = cfg.SceneThreshold
s.authorMenuTemplate = cfg.MenuTemplate // MenuTemplate field doesn't exist in authorConfig struct - remove this line
} }
func (s *appState) persistAuthorConfig() { func (s *appState) persistAuthorConfig() {
@ -116,7 +116,6 @@ func (s *appState) persistAuthorConfig() {
CreateMenu: s.authorCreateMenu, CreateMenu: s.authorCreateMenu,
TreatAsChapters: s.authorTreatAsChapters, TreatAsChapters: s.authorTreatAsChapters,
SceneThreshold: s.authorSceneThreshold, SceneThreshold: s.authorSceneThreshold,
MenuTemplate: s.authorMenuTemplate,
} }
if err := savePersistedAuthorConfig(cfg); err != nil { if err := savePersistedAuthorConfig(cfg); err != nil {
logging.Debug(logging.CatSystem, "failed to persist author config: %v", err) logging.Debug(logging.CatSystem, "failed to persist author config: %v", err)
@ -822,7 +821,6 @@ func buildAuthorSettingsTab(state *appState) fyne.CanvasObject {
CreateMenu: state.authorCreateMenu, CreateMenu: state.authorCreateMenu,
TreatAsChapters: state.authorTreatAsChapters, TreatAsChapters: state.authorTreatAsChapters,
SceneThreshold: state.authorSceneThreshold, SceneThreshold: state.authorSceneThreshold,
MenuTemplate: state.authorMenuTemplate,
} }
if err := savePersistedAuthorConfig(cfg); err != nil { if err := savePersistedAuthorConfig(cfg); err != nil {
dialog.ShowError(fmt.Errorf("failed to save config: %w", err), state.window) 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{}{ config := map[string]interface{}{
"paths": paths, "paths": paths,
"region": region, "region": region,
"aspect": aspect, "aspect": aspect,
"title": title, "title": title,
"outputPath": outputPath, "outputPath": outputPath,
"makeISO": makeISO, "makeISO": makeISO,
"treatAsChapters": s.authorTreatAsChapters, "treatAsChapters": s.authorTreatAsChapters,
"clips": clips, "clips": clips,
"chapters": chapters, "chapters": chapters,
"discSize": s.authorDiscSize, "discSize": s.authorDiscSize,
"outputType": s.authorOutputType, "outputType": s.authorOutputType,
"authorTitle": s.authorTitle, "authorTitle": s.authorTitle,
"authorRegion": s.authorRegion, "authorRegion": s.authorRegion,
"authorAspect": s.authorAspectRatio, "authorAspect": s.authorAspectRatio,
"createMenu": s.authorCreateMenu, "createMenu": s.authorCreateMenu,
"chapterSource": s.authorChapterSource, "chapterSource": s.authorChapterSource,
"subtitleTracks": append([]string{}, s.authorSubtitles...), "subtitleTracks": append([]string{}, s.authorSubtitles...),
"additionalAudios": append([]string{}, s.authorAudioTracks...), "additionalAudios": append([]string{}, s.authorAudioTracks...),
"menuTemplate": s.authorMenuTemplate, "menuTemplate": s.authorMenuTemplate,
"menuBackgroundImage": s.authorMenuBackgroundImage, "menuBackgroundImage": s.authorMenuBackgroundImage,
} }

View File

@ -12,43 +12,43 @@ import (
// Container / Format Colors (File Wrapper) // Container / Format Colors (File Wrapper)
var ( var (
ColorMKV = utils.MustHex("#2563EB") // Deep Blue - Flexible container ColorMKV = utils.MustHex("#3B82F6") // Blue - Flexible container
ColorRemux = utils.MustHex("#9CA3AF") // Neutral Grey - Lossless remux ColorRemux = utils.MustHex("#9CA3AF") // Neutral Grey - Lossless remux
ColorMP4 = utils.MustHex("#1D4ED8") // Navy Blue - Consumer-friendly ColorMP4 = utils.MustHex("#22C55E") // Green - Consumer-friendly
ColorMOV = utils.MustHex("#6366F1") // Indigo - Pro / Apple lineage ColorMOV = utils.MustHex("#A855F7") // Purple - Pro / Apple lineage
ColorAVI = utils.MustHex("#64748B") // Slate - Legacy container ColorAVI = utils.MustHex("#F97316") // Orange - Legacy container
ColorWEBM = utils.MustHex("#059669") // Emerald - Web-native ColorWEBM = utils.MustHex("#14B8A6") // Teal - Web-native
ColorTS = utils.MustHex("#D97706") // Amber - Broadcast / transport streams ColorTS = utils.MustHex("#F59E0B") // Amber - Broadcast / transport streams
ColorM2TS = utils.MustHex("#EA580C") // Orange - Broadcast / transport streams ColorM2TS = utils.MustHex("#EAB308") // Yellow - Broadcast / transport streams
) )
// Video Codec Colors (Compression Method) // Video Codec Colors (Compression Method)
// Modern / Efficient Codecs // Modern / Efficient Codecs
var ( var (
ColorAV1 = utils.MustHex("#22C55E") // Green - Modern, efficient ColorAV1 = utils.MustHex("#F97316") // Orange - Modern, efficient
ColorHEVC = utils.MustHex("#0D9488") // Teal - Modern, efficient ColorHEVC = utils.MustHex("#22C55E") // Green - Modern, efficient
ColorH265 = utils.MustHex("#0D9488") // Teal - Same as HEVC ColorH265 = utils.MustHex("#22C55E") // Green - Same as HEVC
ColorVP9 = utils.MustHex("#06B6D4") // Cyan - Modern, efficient ColorVP9 = utils.MustHex("#8B5CF6") // Violet - Modern, efficient
) )
// Established / Legacy Video Codecs // Established / Legacy Video Codecs
var ( var (
ColorH264 = utils.MustHex("#38BDF8") // Sky - Compatibility ColorH264 = utils.MustHex("#3B82F6") // Blue - Compatibility
ColorAVC = utils.MustHex("#38BDF8") // Sky - Same as H.264 ColorAVC = utils.MustHex("#3B82F6") // Blue - Same as H.264
ColorMPEG2 = utils.MustHex("#FBBF24") // Amber - Legacy / broadcast ColorMPEG2 = utils.MustHex("#EAB308") // Yellow - Legacy / broadcast
ColorDivX = utils.MustHex("#FB7185") // Rose - Legacy ColorDivX = utils.MustHex("#EF4444") // Red - Legacy
ColorXviD = utils.MustHex("#FB7185") // Rose - Legacy ColorXviD = utils.MustHex("#EF4444") // Red - Legacy
ColorMPEG4 = utils.MustHex("#FB7185") // Rose - Legacy ColorMPEG4 = utils.MustHex("#EF4444") // Red - Legacy
) )
// Audio Codec Colors (Secondary but Distinct) // Audio Codec Colors (Secondary but Distinct)
var ( var (
ColorOpus = utils.MustHex("#DB2777") // Magenta - Modern audio ColorOpus = utils.MustHex("#EC4899") // Pink - Modern audio
ColorAAC = utils.MustHex("#FB7185") // Rose - Common audio ColorAAC = utils.MustHex("#06B6D4") // Cyan - Common audio
ColorFLAC = utils.MustHex("#C084FC") // Violet - Lossless audio ColorFLAC = utils.MustHex("#A855F7") // Purple - Lossless audio
ColorMP3 = utils.MustHex("#EF4444") // Red - Legacy audio ColorMP3 = utils.MustHex("#EF4444") // Red - Legacy audio
ColorAC3 = utils.MustHex("#F59E0B") // Amber - Surround 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) // Pixel Format / Colour Data (Technical Metadata)
@ -73,8 +73,10 @@ func GetContainerColor(format string) color.Color {
return ColorAVI return ColorAVI
case "webm": case "webm":
return ColorWEBM return ColorWEBM
case "ts", "m2ts", "mts": case "ts":
return ColorTS return ColorTS
case "m2ts", "mts":
return ColorM2TS
default: default:
return color.RGBA{100, 100, 100, 255} // Default grey return color.RGBA{100, 100, 100, 255} // Default grey
} }

69
main.go
View File

@ -10975,24 +10975,24 @@ func newPlaySession(path string, w, h int, fps, duration float64, targetW, targe
if targetH <= 0 { if targetH <= 0 {
targetH = int(float64(targetW) * (float64(h) / float64(utils.MaxInt(w, 1)))) targetH = int(float64(targetW) * (float64(h) / float64(utils.MaxInt(w, 1))))
} }
// Create UnifiedPlayer adapter for stable A/V playback // Create UnifiedPlayer adapter for stable A/V playback
unifiedAdapter := player.NewUnifiedPlayerAdapter(path, w, h, fps, duration, targetW, targetH, prog, frameFunc, img) unifiedAdapter := player.NewUnifiedPlayerAdapter(path, w, h, fps, duration, targetW, targetH, prog, frameFunc, img)
return &playSession{ return &playSession{
path: path, path: path,
fps: fps, fps: fps,
width: w, width: w,
height: h, height: h,
targetW: targetW, targetW: targetW,
targetH: targetH, targetH: targetH,
volume: 100, volume: 100,
duration: duration, duration: duration,
stop: make(chan struct{}), stop: make(chan struct{}),
done: make(chan struct{}), done: make(chan struct{}),
prog: prog, prog: prog,
frameFunc: frameFunc, frameFunc: frameFunc,
img: img, img: img,
unifiedAdapter: unifiedAdapter, unifiedAdapter: unifiedAdapter,
} }
} }
@ -11000,14 +11000,14 @@ func newPlaySession(path string, w, h int, fps, duration float64, targetW, targe
func (p *playSession) Play() { func (p *playSession) Play() {
p.mu.Lock() p.mu.Lock()
defer p.mu.Unlock() defer p.mu.Unlock()
// Use UnifiedPlayer adapter if available // Use UnifiedPlayer adapter if available
if p.unifiedAdapter != nil { if p.unifiedAdapter != nil {
p.unifiedAdapter.Play() p.unifiedAdapter.Play()
p.paused = false p.paused = false
return return
} }
// Fallback to dual-process // Fallback to dual-process
if p.videoCmd == nil && p.audioCmd == nil { if p.videoCmd == nil && p.audioCmd == nil {
p.startLocked(p.current) p.startLocked(p.current)
@ -11019,14 +11019,14 @@ func (p *playSession) Play() {
func (p *playSession) Pause() { func (p *playSession) Pause() {
p.mu.Lock() p.mu.Lock()
defer p.mu.Unlock() defer p.mu.Unlock()
// Use UnifiedPlayer adapter if available // Use UnifiedPlayer adapter if available
if p.unifiedAdapter != nil { if p.unifiedAdapter != nil {
p.unifiedAdapter.Pause() p.unifiedAdapter.Pause()
p.paused = true p.paused = true
return return
} }
p.paused = true p.paused = true
} }
@ -11036,7 +11036,7 @@ func (p *playSession) Seek(offset float64) {
if offset < 0 { if offset < 0 {
offset = 0 offset = 0
} }
// Use UnifiedPlayer adapter if available // Use UnifiedPlayer adapter if available
if p.unifiedAdapter != nil { if p.unifiedAdapter != nil {
p.unifiedAdapter.Seek(offset) p.unifiedAdapter.Seek(offset)
@ -11044,7 +11044,7 @@ func (p *playSession) Seek(offset float64) {
p.paused = p.unifiedAdapter.IsPlaying() == false p.paused = p.unifiedAdapter.IsPlaying() == false
return return
} }
// Fallback to dual-process // Fallback to dual-process
paused := p.paused paused := p.paused
p.current = offset p.current = offset
@ -11076,7 +11076,7 @@ func (p *playSession) StepFrame(delta int) {
// Use UnifiedPlayer adapter if available // Use UnifiedPlayer adapter if available
if p.unifiedAdapter != nil { if p.unifiedAdapter != nil {
p.unifiedAdapter.StepFrame(delta) p.unifiedAdapter.StepFrame(delta)
p.current = p.unifiedAdapter.GetCurrentFrame() / p.fps p.current = float64(p.unifiedAdapter.GetCurrentFrame()) / p.fps
p.paused = true p.paused = true
return return
} }
@ -11129,12 +11129,12 @@ func (p *playSession) StepFrame(delta int) {
func (p *playSession) GetCurrentFrame() int { func (p *playSession) GetCurrentFrame() int {
p.mu.Lock() p.mu.Lock()
defer p.mu.Unlock() defer p.mu.Unlock()
// Use UnifiedPlayer adapter if available // Use UnifiedPlayer adapter if available
if p.unifiedAdapter != nil { if p.unifiedAdapter != nil {
return p.unifiedAdapter.GetCurrentFrame() return p.unifiedAdapter.GetCurrentFrame()
} }
return p.frameN return p.frameN
} }
@ -11142,13 +11142,13 @@ func (p *playSession) SetVolume(v float64) {
p.mu.Lock() p.mu.Lock()
defer p.mu.Unlock() defer p.mu.Unlock()
p.volume = v p.volume = v
// Use UnifiedPlayer adapter if available // Use UnifiedPlayer adapter if available
if p.unifiedAdapter != nil { if p.unifiedAdapter != nil {
p.unifiedAdapter.SetVolume(v) p.unifiedAdapter.SetVolume(v)
return return
} }
// Fallback to dual-process // Fallback to dual-process
if p.audioCmd != nil && p.audioCmd.Process != nil { if p.audioCmd != nil && p.audioCmd.Process != nil {
// Send volume command to FFmpeg // 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) { func (p *playSession) restartAudio(offset float64) {
p.mu.Lock() p.mu.Lock()
defer p.mu.Unlock() defer p.mu.Unlock()
@ -11173,13 +11180,13 @@ func (p *playSession) restartAudio(offset float64) {
func (p *playSession) Stop() { func (p *playSession) Stop() {
p.mu.Lock() p.mu.Lock()
defer p.mu.Unlock() defer p.mu.Unlock()
// Use UnifiedPlayer adapter if available // Use UnifiedPlayer adapter if available
if p.unifiedAdapter != nil { if p.unifiedAdapter != nil {
p.unifiedAdapter.Stop() p.unifiedAdapter.Stop()
return return
} }
// Fallback to dual-process // Fallback to dual-process
p.stopLocked() p.stopLocked()
} }
@ -11190,7 +11197,7 @@ func (p *playSession) stopLocked() {
p.unifiedAdapter.Stop() p.unifiedAdapter.Stop()
return return
} }
// Fallback to dual-process cleanup // Fallback to dual-process cleanup
select { select {
case <-p.stop: case <-p.stop:
@ -11220,14 +11227,14 @@ func (p *playSession) startLocked(offset float64) {
p.videoTime = offset p.videoTime = offset
p.syncOffset = 0 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) 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 using UnifiedPlayer adapter, no need to run dual-process
if p.unifiedAdapter != nil { if p.unifiedAdapter != nil {
// UnifiedPlayer handles A/V sync internally // UnifiedPlayer handles A/V sync internally
p.unifiedAdapter.Seek(offset) p.unifiedAdapter.Seek(offset)
return return
} }
// Fallback to dual-process (old method) // Fallback to dual-process (old method)
p.runVideo(offset) p.runVideo(offset)
p.runAudio(offset) p.runAudio(offset)