package installer import ( "context" "fmt" "gitgud.io/mike/mpv-manager/pkg/config" "gitgud.io/mike/mpv-manager/pkg/constants" "gitgud.io/mike/mpv-manager/pkg/platform" ) // PlatformInstaller defines the interface for platform-specific installation methods type PlatformInstaller interface { // GetAvailableMethods returns available installation methods GetAvailableMethods(filterInstalled bool) []InstallMethod // Installation methods with output InstallMPVWithOutput(cr *CommandRunner, method string, uiType string) error InstallMPCQTWithOutput(cr *CommandRunner, method string) error InstallMPVAppWithOutput(cr *CommandRunner, uiType string) error InstallMPVViaBrewWithOutput(cr *CommandRunner, uiType string, isUpdate bool) error InstallIINAWithOutput(cr *CommandRunner) error InstallFlatpakWithOutput(cr *CommandRunner, flatpakId string, uiType string, isUpdate bool) error InstallMPVViaPackageWithOutput(cr *CommandRunner, uiType string, isUpdate bool) error InstallCelluloidViaPackageWithOutput(cr *CommandRunner, isUpdate bool) error // Update methods UpdateMPVWithOutput(cr *CommandRunner, method string) error UpdateMPVAppWithOutput(cr *CommandRunner) error // Uninstall methods with output UninstallFlatpakWithOutput(cr *CommandRunner, appId string) error UninstallViaPackageWithOutput(cr *CommandRunner, appName string) error UninstallBrewWithOutput(cr *CommandRunner, appName string) error UninstallAppWithOutput(cr *CommandRunner, appName string) error UninstallWithOutput(cr *CommandRunner) error UninstallMPCQTWithOutput(cr *CommandRunner) error // Windows-specific methods SetupFileAssociationsWithOutput(cr *CommandRunner) error RemoveFileAssociationsWithOutput(cr *CommandRunner) error CreateInstallerShortcutWithOutput(cr *CommandRunner) error CreateInstallerShortcutToBinaryWithOutput(cr *CommandRunner, binaryPath string) error CreateWebUIShortcutWithOutput(cr *CommandRunner) error CreateMPVShortcut(cr *CommandRunner) error } // InstallationHandler provides unified method handling across platforms type InstallationHandler struct { Platform *platform.Platform Installer *Installer PlatformInstaller PlatformInstaller } // ExecuteInstall executes an installation method based on methodID // The context can be used to cancel long-running operations func (ih *InstallationHandler) ExecuteInstall(ctx context.Context, cr *CommandRunner, methodID string, uiType string, isUpdate bool) error { // Check for cancellation at the start select { case <-ctx.Done(): return fmt.Errorf("installation cancelled: %w", ctx.Err()) default: } // Set context on command runner cr.SetContext(ctx) // Handle component methods (UOSC, ModernZ, FFmpeg) - these don't need platform installers switch methodID { case constants.MethodUOSC: installDir := config.GetMPVConfigPath() if isUpdate { return ih.Installer.UpdateUOSCWithOutput(cr, installDir) } return ih.Installer.InstallUOSCWithOutput(cr, installDir) case constants.MethodModernZ: installDir := config.GetMPVConfigPath() if isUpdate { return ih.Installer.UpdateModernZWithOutput(cr, installDir) } return ih.Installer.InstallModernZWithOutput(cr, installDir) case constants.MethodFFmpeg: if !ih.Platform.IsWindows() { return fmt.Errorf("FFmpeg component updates are only supported on Windows") } installDir := config.GetInstallPath() return ih.Installer.UpdateFFmpegWithOutput(cr, installDir) } switch methodID { // Windows methods case constants.MethodMPVBinary, constants.MethodMPVBinaryV3: if ih.PlatformInstaller == nil { return fmt.Errorf("Windows installer not available on %s", ih.Platform.OSType) } if isUpdate { return ih.PlatformInstaller.UpdateMPVWithOutput(cr, methodID) } return ih.PlatformInstaller.InstallMPVWithOutput(cr, methodID, uiType) case constants.MethodMPCQT: if ih.PlatformInstaller == nil { return fmt.Errorf("MPC-QT installer not available on %s", ih.Platform.OSType) } return ih.PlatformInstaller.InstallMPCQTWithOutput(cr, methodID) // macOS methods case constants.MethodMPVApp: if ih.PlatformInstaller == nil { return fmt.Errorf("macOS installer not available on %s", ih.Platform.OSType) } if isUpdate { return ih.PlatformInstaller.UpdateMPVAppWithOutput(cr) } return ih.PlatformInstaller.InstallMPVAppWithOutput(cr, uiType) case constants.MethodMPVBrew: if ih.PlatformInstaller == nil { return fmt.Errorf("macOS installer not available on %s", ih.Platform.OSType) } return ih.PlatformInstaller.InstallMPVViaBrewWithOutput(cr, uiType, isUpdate) case constants.MethodIINA: if ih.PlatformInstaller == nil { return fmt.Errorf("macOS installer not available on %s", ih.Platform.OSType) } return ih.PlatformInstaller.InstallIINAWithOutput(cr) // Linux Flatpak methods case constants.MethodMPVFlatpak: if ih.PlatformInstaller == nil { return fmt.Errorf("Linux installer not available on %s", ih.Platform.OSType) } return ih.PlatformInstaller.InstallFlatpakWithOutput(cr, constants.FlatpakMPV, uiType, isUpdate) case constants.MethodCelluloidFlatpak: if ih.PlatformInstaller == nil { return fmt.Errorf("Linux installer not available on %s", ih.Platform.OSType) } // Celluloid has its own UI, don't install UOSC/ModernZ return ih.PlatformInstaller.InstallFlatpakWithOutput(cr, constants.FlatpakCelluloid, constants.UITypeNone, isUpdate) // Linux Package Manager methods case constants.MethodMPVPackage: if ih.PlatformInstaller == nil { return fmt.Errorf("Linux installer not available on %s", ih.Platform.OSType) } return ih.PlatformInstaller.InstallMPVViaPackageWithOutput(cr, uiType, isUpdate) case constants.MethodCelluloidPackage: if ih.PlatformInstaller == nil { return fmt.Errorf("Linux installer not available on %s", ih.Platform.OSType) } return ih.PlatformInstaller.InstallCelluloidViaPackageWithOutput(cr, isUpdate) default: return fmt.Errorf("unknown installation method: %s", methodID) } } // ExecuteUninstall executes an uninstallation method based on app info // The context can be used to cancel long-running operations func (ih *InstallationHandler) ExecuteUninstall(ctx context.Context, cr *CommandRunner, app *config.InstalledApp) error { // Check for cancellation at the start select { case <-ctx.Done(): return fmt.Errorf("uninstallation cancelled: %w", ctx.Err()) default: } // Set context on command runner cr.SetContext(ctx) methodID := app.InstallMethod // Windows methods if methodID == constants.MethodMPVBinary || methodID == constants.MethodMPVBinaryV3 { if ih.PlatformInstaller == nil { return fmt.Errorf("Windows installer not available on %s", ih.Platform.OSType) } return ih.PlatformInstaller.UninstallWithOutput(cr) } if methodID == constants.MethodMPCQT { if ih.PlatformInstaller == nil { return fmt.Errorf("Windows installer not available on %s", ih.Platform.OSType) } return ih.PlatformInstaller.UninstallMPCQTWithOutput(cr) } // macOS methods if methodID == constants.MethodMPVApp { if ih.PlatformInstaller == nil { return fmt.Errorf("macOS installer not available on %s", ih.Platform.OSType) } return ih.PlatformInstaller.UninstallAppWithOutput(cr, "MPV") } if methodID == constants.MethodIINA { if ih.PlatformInstaller == nil { return fmt.Errorf("macOS installer not available on %s", ih.Platform.OSType) } return ih.PlatformInstaller.UninstallAppWithOutput(cr, "IINA") } if methodID == constants.MethodMPVBrew { if ih.PlatformInstaller == nil { return fmt.Errorf("macOS installer not available on %s", ih.Platform.OSType) } return ih.PlatformInstaller.UninstallBrewWithOutput(cr, "mpv") } // Linux Flatpak methods if methodID == constants.MethodMPVFlatpak { if ih.PlatformInstaller == nil { return fmt.Errorf("Linux installer not available on %s", ih.Platform.OSType) } return ih.PlatformInstaller.UninstallFlatpakWithOutput(cr, constants.FlatpakMPV) } if methodID == constants.MethodCelluloidFlatpak { if ih.PlatformInstaller == nil { return fmt.Errorf("Linux installer not available on %s", ih.Platform.OSType) } return ih.PlatformInstaller.UninstallFlatpakWithOutput(cr, constants.FlatpakCelluloid) } // Linux Package Manager methods if methodID == constants.MethodMPVPackage { if ih.PlatformInstaller == nil { return fmt.Errorf("Linux installer not available on %s", ih.Platform.OSType) } return ih.PlatformInstaller.UninstallViaPackageWithOutput(cr, constants.PackageMPV) } if methodID == constants.MethodCelluloidPackage { if ih.PlatformInstaller == nil { return fmt.Errorf("Linux installer not available on %s", ih.Platform.OSType) } return ih.PlatformInstaller.UninstallViaPackageWithOutput(cr, constants.PackageCelluloid) } return fmt.Errorf("unknown installation method: %s", methodID) } // ExecuteUpdate executes an update operation based on methodID // This is a convenience wrapper around ExecuteInstall with isUpdate=true func (ih *InstallationHandler) ExecuteUpdate(ctx context.Context, cr *CommandRunner, methodID string) error { return ih.ExecuteInstall(ctx, cr, methodID, constants.DefaultUIType, true) } // SetupFileAssociationsWithOutput creates file association shortcuts on Desktop // This is Windows-specific and creates shortcuts that user runs as Administrator func (ih *InstallationHandler) SetupFileAssociationsWithOutput(cr *CommandRunner) error { if ih.Platform.IsWindows() { if ih.PlatformInstaller == nil { return fmt.Errorf("Windows installer not available") } return ih.PlatformInstaller.SetupFileAssociationsWithOutput(cr) } return fmt.Errorf("file associations are only supported on Windows") } // RemoveFileAssociationsWithOutput removes file associations // This is Windows-specific and runs the uninstall batch file func (ih *InstallationHandler) RemoveFileAssociationsWithOutput(cr *CommandRunner) error { if ih.Platform.IsWindows() { if ih.PlatformInstaller == nil { return fmt.Errorf("Windows installer not available") } return ih.PlatformInstaller.RemoveFileAssociationsWithOutput(cr) } return fmt.Errorf("file associations are only supported on Windows") } // CreateInstallerShortcutWithOutput creates a Desktop shortcut to the installer binary // This is Windows-specific and creates a shortcut for easy access to the installer func (ih *InstallationHandler) CreateInstallerShortcutWithOutput(cr *CommandRunner) error { if ih.Platform.IsWindows() { if ih.PlatformInstaller == nil { return fmt.Errorf("Windows installer not available") } return ih.PlatformInstaller.CreateInstallerShortcutWithOutput(cr) } if ih.Platform.IsDarwin() { if ih.PlatformInstaller == nil { return fmt.Errorf("macOS installer not available") } return ih.PlatformInstaller.CreateInstallerShortcutWithOutput(cr) } if ih.Platform.IsLinux() { if ih.PlatformInstaller == nil { return fmt.Errorf("Linux installer not available") } return ih.PlatformInstaller.CreateInstallerShortcutWithOutput(cr) } return fmt.Errorf("installer shortcut creation not supported") } // CreateWebUIShortcutWithOutput creates a shortcut to launch installer in Web UI mode // Creates platform-specific shortcuts (Windows .lnk, Linux .desktop, macOS .app) func (ih *InstallationHandler) CreateWebUIShortcutWithOutput(cr *CommandRunner) error { if ih.Platform.IsWindows() { if ih.PlatformInstaller == nil { return fmt.Errorf("Windows installer not available") } return ih.PlatformInstaller.CreateWebUIShortcutWithOutput(cr) } else if ih.Platform.IsDarwin() { if ih.PlatformInstaller == nil { return fmt.Errorf("macOS installer not available") } return ih.PlatformInstaller.CreateWebUIShortcutWithOutput(cr) } else if ih.Platform.IsLinux() { if ih.PlatformInstaller == nil { return fmt.Errorf("Linux installer not available") } return ih.PlatformInstaller.CreateWebUIShortcutWithOutput(cr) } return fmt.Errorf("Web UI shortcut creation not supported on this platform") } // CreateMPVShortcutWithOutput creates Desktop and Start Menu shortcuts for MPV // This is Windows-specific func (ih *InstallationHandler) CreateMPVShortcutWithOutput(cr *CommandRunner) error { if !ih.Platform.IsWindows() { return fmt.Errorf("MPV shortcuts are only supported on Windows") } if ih.PlatformInstaller == nil { return fmt.Errorf("Windows installer not available") } return ih.PlatformInstaller.CreateMPVShortcut(cr) }