// Package blender handles Blender binary management and execution. package blender import ( "fmt" "log" "os" "path/filepath" "jiggablend/internal/runner/api" "jiggablend/internal/runner/workspace" ) // Manager handles Blender binary downloads and management. type Manager struct { manager *api.ManagerClient workspaceDir string } // NewManager creates a new Blender manager. func NewManager(managerClient *api.ManagerClient, workspaceDir string) *Manager { return &Manager{ manager: managerClient, workspaceDir: workspaceDir, } } // GetBinaryPath returns the path to the Blender binary for a specific version. // Downloads from manager and extracts if not already present. func (m *Manager) GetBinaryPath(version string) (string, error) { blenderDir := filepath.Join(m.workspaceDir, "blender-versions") if err := os.MkdirAll(blenderDir, 0755); err != nil { return "", fmt.Errorf("failed to create blender directory: %w", err) } // Check if already installed - look for version folder first versionDir := filepath.Join(blenderDir, version) binaryPath := filepath.Join(versionDir, "blender") // Check if version folder exists and contains the binary if versionInfo, err := os.Stat(versionDir); err == nil && versionInfo.IsDir() { // Version folder exists, check if binary is present if binaryInfo, err := os.Stat(binaryPath); err == nil { // Verify it's actually a file (not a directory) if !binaryInfo.IsDir() { log.Printf("Found existing Blender %s installation at %s", version, binaryPath) return binaryPath, nil } } // Version folder exists but binary is missing - might be incomplete installation log.Printf("Version folder %s exists but binary not found, will re-download", versionDir) } // Download from manager log.Printf("Downloading Blender %s from manager", version) reader, err := m.manager.DownloadBlender(version) if err != nil { return "", err } defer reader.Close() // Manager serves pre-decompressed .tar files - extract directly log.Printf("Extracting Blender %s...", version) if err := workspace.ExtractTarStripPrefix(reader, versionDir); err != nil { return "", fmt.Errorf("failed to extract blender: %w", err) } // Verify binary exists if _, err := os.Stat(binaryPath); err != nil { return "", fmt.Errorf("blender binary not found after extraction") } log.Printf("Blender %s installed at %s", version, binaryPath) return binaryPath, nil } // GetBinaryForJob returns the Blender binary path for a job. // Uses the version from metadata or falls back to system blender. func (m *Manager) GetBinaryForJob(version string) (string, error) { if version == "" { return "blender", nil // System blender } return m.GetBinaryPath(version) }