| 1 | //go:build !cgolottie |
| 2 | |
| 3 | package helper |
| 4 | |
| 5 | import ( |
| 6 | "os" |
| 7 | "os/exec" |
| 8 | |
| 9 | "github.com/sirupsen/logrus" |
| 10 | ) |
| 11 | |
| 12 | // CanConvertTgsToX Checks whether the external command necessary for ConvertTgsToX works. |
| 13 | func CanConvertTgsToX() error { |
| 14 | // We depend on the fact that `lottie_convert.py --help` has exit status 0. |
| 15 | // Hyrum's Law predicted this, and Murphy's Law predicts that this will break eventually. |
| 16 | // However, there is no alternative like `lottie_convert.py --is-properly-installed` |
| 17 | cmd := exec.Command("lottie_convert.py", "--help") |
| 18 | return cmd.Run() |
| 19 | } |
| 20 | |
| 21 | // ConvertTgsToWebP convert input data (which should be tgs format) to WebP format |
| 22 | // This relies on an external command, which is ugly, but works. |
| 23 | func ConvertTgsToX(data *[]byte, outputFormat string, logger *logrus.Entry) error { |
| 24 | // lottie can't handle input from a pipe, so write to a temporary file: |
| 25 | tmpInFile, err := os.CreateTemp(os.TempDir(), "matterbridge-lottie-input-*.tgs") |
| 26 | if err != nil { |
| 27 | return err |
| 28 | } |
| 29 | tmpInFileName := tmpInFile.Name() |
| 30 | defer func() { |
| 31 | if removeErr := os.Remove(tmpInFileName); removeErr != nil { |
| 32 | logger.Errorf("Could not delete temporary (input) file %s: %v", tmpInFileName, removeErr) |
| 33 | } |
| 34 | }() |
| 35 | // lottie can handle writing to a pipe, but there is no way to do that platform-independently. |
| 36 | // "/dev/stdout" won't work on Windows, and "-" upsets Cairo for some reason. So we need another file: |
| 37 | tmpOutFile, err := os.CreateTemp(os.TempDir(), "matterbridge-lottie-output-*.data") |
| 38 | if err != nil { |
| 39 | return err |
| 40 | } |
| 41 | tmpOutFileName := tmpOutFile.Name() |
| 42 | defer func() { |
| 43 | if removeErr := os.Remove(tmpOutFileName); removeErr != nil { |
| 44 | logger.Errorf("Could not delete temporary (output) file %s: %v", tmpOutFileName, removeErr) |
| 45 | } |
| 46 | }() |
| 47 | |
| 48 | if _, writeErr := tmpInFile.Write(*data); writeErr != nil { |
| 49 | return writeErr |
| 50 | } |
| 51 | // Must close before calling lottie to avoid data races: |
| 52 | if closeErr := tmpInFile.Close(); closeErr != nil { |
| 53 | return closeErr |
| 54 | } |
| 55 | |
| 56 | // Call lottie to transform: |
| 57 | cmd := exec.Command("lottie_convert.py", "--input-format", "lottie", "--output-format", outputFormat, tmpInFileName, tmpOutFileName) |
| 58 | cmd.Stdout = nil |
| 59 | cmd.Stderr = nil |
| 60 | // NB: lottie writes progress into to stderr in all cases. |
| 61 | _, stderr := cmd.Output() |
| 62 | if stderr != nil { |
| 63 | // 'stderr' already contains some parts of Stderr, because it was set to 'nil'. |
| 64 | return stderr |
| 65 | } |
| 66 | |
| 67 | dataContents, err := os.ReadFile(tmpOutFileName) //nolint:gosec |
| 68 | if err != nil { |
| 69 | return err |
| 70 | } |
| 71 | |
| 72 | *data = dataContents |
| 73 | return nil |
| 74 | } |
| 75 | |
| 76 | func SupportsFormat(format string) bool { |
| 77 | switch format { |
| 78 | case "png": |
| 79 | fallthrough |
| 80 | case "webp": |
| 81 | return true |
| 82 | default: |
| 83 | return false |
| 84 | } |
| 85 | } |
| 86 | |
| 87 | func LottieBackend() string { |
| 88 | return "lottie_convert.py" |
| 89 | } |
| 90 | |