diff --git a/internal/worker/command_blender.go b/internal/worker/command_blender.go index bc492be3..ced7e431 100644 --- a/internal/worker/command_blender.go +++ b/internal/worker/command_blender.go @@ -15,6 +15,7 @@ import ( "github.com/google/shlex" "github.com/rs/zerolog" + "git.blender.org/flamenco/internal/worker/find_blender" "git.blender.org/flamenco/pkg/api" ) @@ -117,7 +118,7 @@ func (ce *CommandExecutor) cmdBlenderRenderCommand( // Attempt a platform-specific way to find which Blender executable to // use. If Blender cannot not be found, just use the configured command // and let the OS produce the errors. - path, err := FindBlender() + path, err := find_blender.FindBlender() if err == nil { logger.Info().Str("path", path).Msg("found Blender") parameters.exe = path diff --git a/internal/worker/command_blender_nonwindows.go b/internal/worker/find_blender/nonwindows.go similarity index 93% rename from internal/worker/command_blender_nonwindows.go rename to internal/worker/find_blender/nonwindows.go index 071de15a..90957561 100644 --- a/internal/worker/command_blender_nonwindows.go +++ b/internal/worker/find_blender/nonwindows.go @@ -1,7 +1,7 @@ //go:build !windows // SPDX-License-Identifier: GPL-3.0-or-later -package worker +package find_blender import "errors" diff --git a/internal/worker/command_blender_windows.go b/internal/worker/find_blender/windows.go similarity index 78% rename from internal/worker/command_blender_windows.go rename to internal/worker/find_blender/windows.go index b570ccb9..82aadc9f 100644 --- a/internal/worker/command_blender_windows.go +++ b/internal/worker/find_blender/windows.go @@ -1,16 +1,45 @@ //go:build windows // SPDX-License-Identifier: GPL-3.0-or-later -package worker +package find_blender import ( "fmt" + "os" + "path/filepath" "syscall" "unsafe" ) -// FindBlender returns the path of `blender.exe` associated with ".blend" files. +// FindBlender returns the full path of `blender.exe` associated with ".blend" files. func FindBlender() (string, error) { + exe, err := fileAssociation(".blend") + if err != nil { + return "", err + } + + // Often the association will be with blender-launcher.exe, which is + // unsuitable for use in Flamenco. Use its path to find its `blender.exe`. + dir, file := filepath.Split(exe) + if file != "blender-launcher.exe" { + return exe, nil + } + + blenderPath := filepath.Join(dir, "blender.exe") + _, err = os.Stat(blenderPath) + if err != nil { + if os.IsNotExist(err) { + return "", fmt.Errorf("blender-launcher found at %s but not its blender.exe", exe) + } + return "", fmt.Errorf("investigating %s: %w", blenderPath, err) + } + + return blenderPath, nil +} + +// fileAssociation finds the executable associated with the given extension. +// The extension must be a string like ".blend". +func fileAssociation(extension string) (string, error) { // Load library. libname := "shlwapi.dll" libshlwapi, err := syscall.LoadLibrary(libname) @@ -27,7 +56,7 @@ func FindBlender() (string, error) { } // https://docs.microsoft.com/en-gb/windows/win32/api/shlwapi/nf-shlwapi-assocquerystringw - pszAssoc, err := syscall.UTF16PtrFromString(".blend") + pszAssoc, err := syscall.UTF16PtrFromString(extension) if err != nil { return "", fmt.Errorf("converting string to UTF16: %w", err) } diff --git a/internal/worker/command_blender_windows_test.go b/internal/worker/find_blender/windows_test.go similarity index 67% rename from internal/worker/command_blender_windows_test.go rename to internal/worker/find_blender/windows_test.go index b05e15b3..618cc345 100644 --- a/internal/worker/command_blender_windows_test.go +++ b/internal/worker/find_blender/windows_test.go @@ -1,7 +1,7 @@ //go:build windows // SPDX-License-Identifier: GPL-3.0-or-later -package worker +package find_blender import ( "testing" @@ -13,9 +13,12 @@ import ( // It would be too fragile to always require a file association to be set up with Blender. func TestFindBlender(t *testing.T) { exe, err := FindBlender() - if err == nil { - assert.NotEmpty(t, exe) - } else { + if err != nil { assert.Empty(t, exe) + return } + assert.NotEmpty(t, exe) + assert.NotContains(t, exe, + "blender-launcher", + "FindBlender should find blender.exe, not blender-launcher.exe") }