package tui import ( "runtime" "github.com/charmbracelet/bubbles/list" "gitgud.io/mike/mpv-manager/pkg/config" "gitgud.io/mike/mpv-manager/pkg/constants" "gitgud.io/mike/mpv-manager/pkg/platform" ) func (m *Model) setupConfigOptionsList() { items := []list.Item{} installedApps := config.GetInstalledApps() hasMPVApp := hasMPVInstallMethod(installedApps) if runtime.GOOS == "windows" { hasMPVBinary := false for _, app := range installedApps { if app.InstallMethod == "mpv-binary" || app.InstallMethod == "mpv-binary-v3" { hasMPVBinary = true break } } if hasMPVBinary { items = append(items, menuItem{ id: "file-associations", title: "🔗 Configure File Associations", description: "Set up double-click video file opening", recommended: false, }) } } // Add Screenshot Settings option (always available) items = append(items, menuItem{ id: "screenshot-settings", title: "📷 Screenshot Settings", description: "Configure screenshot format and options", recommended: false, }) // Add Change MPV UI option if an MPV app is installed if hasMPVApp { items = append(items, menuItem{ id: "change-mpv-ui", title: "🎨 Change MPV UI", description: "Switch between ModernZ, UOSC, or default MPV interface", recommended: false, }) } // Add Save Position on Quit option (always available) items = append(items, menuItem{ id: "save-position-on-quit", title: "💾 Save Position on Quit", description: getSavePositionOnQuitDescription(), recommended: false, }) // Add Change Hardware Acceleration option items = append(items, menuItem{ id: "change-hwdec", title: "⚡ Change Hardware Acceleration", description: "Select hardware decoding method for MPV", recommended: false, }) // Add Reset Config option items = append(items, menuItem{ id: "reset-config", title: "🔄 Reset MPV Config to Recommended", description: "Restore mpv.conf to latest recommended configuration", recommended: false, }) // Add Restore Config option items = append(items, menuItem{ id: "restore-config", title: "â†Šī¸ Restore MPV Config", description: "Restore from a backup configuration", recommended: false, }) m.configOptionsList = m.createStyledList("MPV Configuration Options", items) } func (m *Model) setupHWAOptionsList() { p := m.platformInfo var brand string if m.platformInfo != nil { if m.platformInfo.GPUInfo != nil { brand = m.platformInfo.GPUInfo.Brand } } hwaOptions := platform.GetHWAOptions(p.OSType, brand) // Get the recommended HWA decoder based on platform and GPU recommendedHWA := platform.GetRecommendedHWADecoder(p.OSType, brand) items := []list.Item{ hwaOptionItem{ id: "auto", title: "auto", description: platform.GetHwaDescription("auto"), recommended: recommendedHWA == "auto", }, } for _, opt := range hwaOptions { if opt != "auto" { items = append(items, hwaOptionItem{ id: opt, title: opt, description: platform.GetHwaDescription(opt), recommended: opt == recommendedHWA, }) } } // Add "no" option at the end for all platforms items = append(items, hwaOptionItem{ id: "no", title: "no", description: platform.GetHwaDescription("no"), recommended: false, }) m.hwaOptionsList = m.createStyledList("Hardware Acceleration", items) } func (m *Model) setupFileAssociationsList() { items := []list.Item{ menuItem{ id: "install-associations", title: "🔗 Install File Associations", description: "Set MPV as default for video files", recommended: true, }, menuItem{ id: "remove-associations", title: "❌ Remove File Associations", description: "Remove MPV as default for video files", recommended: false, }, } m.fileAssociationsList = m.createStyledList("File Associations", items) } func (m *Model) getCurrentHWAValue() string { values := config.GetConfigValue(constants.ConfigKeyHardwareDecoder) if len(values) == 0 { return "auto" } return values[0] } func (m *Model) applyHWAOption(option string) error { // Save to mpv.conf if err := config.SetConfigValue(constants.ConfigKeyHardwareDecoder, []string{option}, false); err != nil { return err } // Also save preference to mpv-manager.json for backup/restore if err := config.SetHWADecoderPreference(option); err != nil { // Log warning but don't fail // The preference is optional, mpv.conf is the source of truth } return nil } func (m *Model) setupScreenshotOptionsList() { items := []list.Item{ screenshotOptionItem{ id: "screenshot-format", title: "Screenshot Format", description: getCurrentScreenshotValue("format"), recommended: false, }, screenshotOptionItem{ id: "screenshot-tag-colorspace", title: "Tag Colorspace in Filename", description: getCurrentScreenshotValue("tag-colorspace"), recommended: false, }, screenshotOptionItem{ id: "screenshot-high-bit-depth", title: "High Bit Depth", description: getCurrentScreenshotValue("high-bit-depth"), recommended: false, }, screenshotOptionItem{ id: "screenshot-template", title: "Screenshot Filename Template", description: getCurrentScreenshotValue("template"), recommended: false, }, screenshotOptionItem{ id: "screenshot-directory", title: "Screenshot Directory", description: getCurrentScreenshotValue("directory"), recommended: false, }, screenshotOptionItem{ id: "screenshot-jpeg-quality", title: "JPEG Quality", description: getCurrentScreenshotValue("jpeg-quality"), recommended: false, }, screenshotOptionItem{ id: "screenshot-png-compression", title: "PNG Compression", description: getCurrentScreenshotValue("png-compression"), recommended: false, }, screenshotOptionItem{ id: "screenshot-webp-lossless", title: "WebP Lossless", description: getCurrentScreenshotValue("webp-lossless"), recommended: false, }, screenshotOptionItem{ id: "screenshot-webp-quality", title: "WebP Quality", description: getCurrentScreenshotValue("webp-quality"), recommended: false, }, screenshotOptionItem{ id: "screenshot-jxl-distance", title: "JPEG XL Distance", description: getCurrentScreenshotValue("jxl-distance"), recommended: false, }, screenshotOptionItem{ id: "screenshot-avif-encoder", title: "AVIF Encoder", description: getCurrentScreenshotValue("avif-encoder"), recommended: false, }, screenshotOptionItem{ id: "screenshot-avif-pixfmt", title: "AVIF Pixel Format", description: getCurrentScreenshotValue("avif-pixfmt"), recommended: false, }, screenshotOptionItem{ id: "screenshot-avif-opts", title: "AVIF Encoder Options", description: getCurrentScreenshotValue("avif-opts"), recommended: false, }, } m.screenshotOptionsList = m.createStyledList("Screenshot Settings", items) } func getCurrentScreenshotValue(option string) string { var key string var defaultValue string switch option { case "format": key = constants.ConfigKeyScreenshotFormat defaultValue = "png" case "tag-colorspace": key = constants.ConfigKeyScreenshotTagColorspace defaultValue = "yes" case "high-bit-depth": key = constants.ConfigKeyScreenshotHighBitDepth defaultValue = "yes" case "template": key = constants.ConfigKeyScreenshotTemplate defaultValue = "mpv-shot%n" case "directory": key = constants.ConfigKeyScreenshotDirectory defaultValue = "" case "jpeg-quality": key = constants.ConfigKeyScreenshotJpegQuality defaultValue = "90" case "png-compression": key = constants.ConfigKeyScreenshotPngCompression defaultValue = "7" case "webp-lossless": key = constants.ConfigKeyScreenshotWebpLossless defaultValue = "no" case "webp-quality": key = constants.ConfigKeyScreenshotWebpQuality defaultValue = "75" case "jxl-distance": key = constants.ConfigKeyScreenshotJxlDistance defaultValue = "1.0" case "avif-encoder": key = constants.ConfigKeyScreenshotAvifEncoder defaultValue = "libaom-av1" case "avif-pixfmt": key = constants.ConfigKeyScreenshotAvifPixfmt defaultValue = "" case "avif-opts": key = constants.ConfigKeyScreenshotAvifOpts defaultValue = "usage=allintra,crf=0,cpu-used=8" } values := config.GetConfigValue(key) if len(values) == 0 { if defaultValue == "" { return "Not set" } return defaultValue + " (default)" } return values[0] } // screenshotOptionItem implements list.Item for screenshot options type screenshotOptionItem struct { id string title string description string recommended bool } func (s screenshotOptionItem) Title() string { return s.title } func (s screenshotOptionItem) Description() string { return s.description } func (s screenshotOptionItem) FilterValue() string { return s.title } // Screenshot value selection lists func (m *Model) setupScreenshotFormatList() { items := []list.Item{ screenshotValueItem{id: "png", title: "png", description: "PNG format (lossless)"}, screenshotValueItem{id: "jpg", title: "jpg", description: "JPEG format (lossy)"}, screenshotValueItem{id: "jpeg", title: "jpeg", description: "JPEG format (lossy)"}, screenshotValueItem{id: "webp", title: "webp", description: "WebP format (lossless/lossy)"}, screenshotValueItem{id: "jxl", title: "jxl", description: "JPEG XL format (lossless/lossy)"}, screenshotValueItem{id: "avif", title: "avif", description: "AVIF format (lossless/lossy)"}, screenshotValueItem{id: "none", title: "none", description: "Disable screenshots"}, } m.screenshotValueList = m.createStyledList("Select Screenshot Format", items) } func (m *Model) setupScreenshotYesNoList(optionName, title string) { items := []list.Item{ screenshotValueItem{id: "yes", title: "yes", description: "Enabled"}, screenshotValueItem{id: "no", title: "no", description: "Disabled"}, } m.screenshotValueList = m.createStyledList(title, items) } func (m *Model) setupScreenshotNumericList(optionName, title string, values []string) { items := []list.Item{} for _, v := range values { items = append(items, screenshotValueItem{id: v, title: v, description: ""}) } m.screenshotValueList = m.createStyledList(title, items) } func (m *Model) setupScreenshotEncoderList() { items := []list.Item{ screenshotValueItem{id: "libaom-av1", title: "libaom-av1", description: "AOMedia AV1 encoder (slow, best quality)"}, screenshotValueItem{id: "rav1e", title: "rav1e", description: "Rav1e AV1 encoder (balanced)"}, screenshotValueItem{id: "svt-av1", title: "svt-av1", description: "SVT-AV1 encoder (fast, good quality)"}, } m.screenshotValueList = m.createStyledList("Select AVIF Encoder", items) } func (m *Model) setupScreenshotPixelFormatList() { items := []list.Item{ screenshotValueItem{id: "yuv420p", title: "yuv420p", description: "4:2:0 8-bit"}, screenshotValueItem{id: "yuv422p", title: "yuv422p", description: "4:2:2 8-bit"}, screenshotValueItem{id: "yuv444p", title: "yuv444p", description: "4:4:4 8-bit"}, screenshotValueItem{id: "yuv420p10", title: "yuv420p10", description: "4:2:0 10-bit"}, screenshotValueItem{id: "yuv420p12", title: "yuv420p12", description: "4:2:0 12-bit"}, screenshotValueItem{id: "yuv422p10", title: "yuv422p10", description: "4:2:2 10-bit"}, screenshotValueItem{id: "yuv422p12", title: "yuv422p12", description: "4:2:2 12-bit"}, screenshotValueItem{id: "yuv444p10", title: "yuv444p10", description: "4:4:4 10-bit"}, screenshotValueItem{id: "yuv444p12", title: "yuv444p12", description: "4:4:4 12-bit"}, } m.screenshotValueList = m.createStyledList("Select Pixel Format", items) } // screenshotValueItem implements list.Item for screenshot value selection type screenshotValueItem struct { id string title string description string } func (s screenshotValueItem) Title() string { return s.title } func (s screenshotValueItem) Description() string { return s.description } func (s screenshotValueItem) FilterValue() string { return s.title } // Function to get screenshot config key from option ID func getScreenshotConfigKey(optionID string) string { switch optionID { case "screenshot-format": return constants.ConfigKeyScreenshotFormat case "screenshot-tag-colorspace": return constants.ConfigKeyScreenshotTagColorspace case "screenshot-high-bit-depth": return constants.ConfigKeyScreenshotHighBitDepth case "screenshot-template": return constants.ConfigKeyScreenshotTemplate case "screenshot-directory": return constants.ConfigKeyScreenshotDirectory case "screenshot-jpeg-quality": return constants.ConfigKeyScreenshotJpegQuality case "screenshot-png-compression": return constants.ConfigKeyScreenshotPngCompression case "screenshot-webp-lossless": return constants.ConfigKeyScreenshotWebpLossless case "screenshot-webp-quality": return constants.ConfigKeyScreenshotWebpQuality case "screenshot-jxl-distance": return constants.ConfigKeyScreenshotJxlDistance case "screenshot-avif-encoder": return constants.ConfigKeyScreenshotAvifEncoder case "screenshot-avif-pixfmt": return constants.ConfigKeyScreenshotAvifPixfmt case "screenshot-avif-opts": return constants.ConfigKeyScreenshotAvifOpts default: return "" } } // getSavePositionOnQuitDescription returns the current save-position-on-quit setting description func getSavePositionOnQuitDescription() string { values := config.GetConfigValue(constants.ConfigKeySavePositionOnQuit) if len(values) == 0 || values[0] == "" { return "no (default) - Resume playback position is disabled" } if values[0] == "yes" { return "yes - Remember playback position when you quit" } return values[0] + " - Resume playback position is disabled" } // setupSavePositionOnQuitList creates the yes/no selection list func (m *Model) setupSavePositionOnQuitList() { items := []list.Item{ screenshotValueItem{id: "no", title: "no", description: "Disabled (default) - Don't remember position"}, screenshotValueItem{id: "yes", title: "yes", description: "Enabled - Resume where you left off"}, } m.screenshotValueList = m.createStyledList("Save Position on Quit", items) } // hasMPVInstallMethod checks if any MPV install method exists in installed apps // This excludes Celluloid, IINA, MPC-QT, and Infuse which have their own UIs func hasMPVInstallMethod(apps []config.InstalledApp) bool { mpvMethods := []string{ "mpv-flatpak", "mpv-package", "mpv-binary", "mpv-binary-v3", "mpv-app", "mpv-brew", } for _, app := range apps { for _, method := range mpvMethods { if app.InstallMethod == method { return true } } } return false }