summaryrefslogtreecommitdiffstats
path: root/bridge/helper/lottie_convert.go
blob: ffbe95d75439349b265680186b8988bd3f79a947 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
//go:build !cgolottie

package helper

import (
	"io/ioutil"
	"os"
	"os/exec"

	"github.com/sirupsen/logrus"
)

// CanConvertTgsToX Checks whether the external command necessary for ConvertTgsToX works.
func CanConvertTgsToX() error {
	// We depend on the fact that `lottie_convert.py --help` has exit status 0.
	// Hyrum's Law predicted this, and Murphy's Law predicts that this will break eventually.
	// However, there is no alternative like `lottie_convert.py --is-properly-installed`
	cmd := exec.Command("lottie_convert.py", "--help")
	return cmd.Run()
}

// ConvertTgsToWebP convert input data (which should be tgs format) to WebP format
// This relies on an external command, which is ugly, but works.
func ConvertTgsToX(data *[]byte, outputFormat string, logger *logrus.Entry) error {
	// lottie can't handle input from a pipe, so write to a temporary file:
	tmpInFile, err := ioutil.TempFile(os.TempDir(), "matterbridge-lottie-input-*.tgs")
	if err != nil {
		return err
	}
	tmpInFileName := tmpInFile.Name()
	defer func() {
		if removeErr := os.Remove(tmpInFileName); removeErr != nil {
			logger.Errorf("Could not delete temporary (input) file %s: %v", tmpInFileName, removeErr)
		}
	}()
	// lottie can handle writing to a pipe, but there is no way to do that platform-independently.
	// "/dev/stdout" won't work on Windows, and "-" upsets Cairo for some reason. So we need another file:
	tmpOutFile, err := ioutil.TempFile(os.TempDir(), "matterbridge-lottie-output-*.data")
	if err != nil {
		return err
	}
	tmpOutFileName := tmpOutFile.Name()
	defer func() {
		if removeErr := os.Remove(tmpOutFileName); removeErr != nil {
			logger.Errorf("Could not delete temporary (output) file %s: %v", tmpOutFileName, removeErr)
		}
	}()

	if _, writeErr := tmpInFile.Write(*data); writeErr != nil {
		return writeErr
	}
	// Must close before calling lottie to avoid data races:
	if closeErr := tmpInFile.Close(); closeErr != nil {
		return closeErr
	}

	// Call lottie to transform:
	cmd := exec.Command("lottie_convert.py", "--input-format", "lottie", "--output-format", outputFormat, tmpInFileName, tmpOutFileName)
	cmd.Stdout = nil
	cmd.Stderr = nil
	// NB: lottie writes progress into to stderr in all cases.
	_, stderr := cmd.Output()
	if stderr != nil {
		// 'stderr' already contains some parts of Stderr, because it was set to 'nil'.
		return stderr
	}
	dataContents, err := ioutil.ReadFile(tmpOutFileName)
	if err != nil {
		return err
	}

	*data = dataContents
	return nil
}

func SupportsFormat(format string) bool {
	switch format {
	case "png":
		fallthrough
	case "webp":
		return true
	default:
		return false
	}
	return false
}

func LottieBackend() string {
	return "lottie_convert.py"
}