From 919b52d9be4d785b891e51b0124917d1a7c1842f Mon Sep 17 00:00:00 2001
From: Travis Ralston <travpc@gmail.com>
Date: Sun, 16 Aug 2020 12:44:04 -0600
Subject: [PATCH] Add preliminary support for MP4 files

This needs to be expanded to cover more of what ffmpeg covers. This does not support animation for a few reasons:
* Most clients which will be using a video thumbnail will be using it in such a way where even a GIF isn't supported.
* Clients rely on the thumbnail endpoint returning images, which can lead to resource inefficiencies if we return GIFs.
* Returning more optimized thumbnails is not feasible for compatibility with most clients.
---
 CHANGELOG.md          |  1 +
 config.sample.yaml    |  1 +
 thumbnailing/i/mp4.go | 66 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 68 insertions(+)
 create mode 100644 thumbnailing/i/mp4.go

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 19136c43..39a155f4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
   * After running this tool, use the `gdpr_import` tool to bring the export into the media repo.
 * Added thumbnailing support for some audio waveforms (MP3, WAV, OGG, and FLAC).
 * Added audio metadata (duration, etc) to the unstable `/info` endpoint. Aligns with [MSC2380](https://github.com/matrix-org/matrix-doc/pull/2380).
+* Added simple thumbnailing for MP4 videos.
 
 ### Fixed
 
diff --git a/config.sample.yaml b/config.sample.yaml
index 78d4366a..f237af47 100644
--- a/config.sample.yaml
+++ b/config.sample.yaml
@@ -397,6 +397,7 @@ thumbnails:
     - "audio/ogg"
     - "audio/wav"
     - "audio/flac"
+    #- "video/mp4" # Be sure to have ffmpeg installed to thumbnail video files
 
   # Animated thumbnails can be CPU intensive to generate. To disable the generation of animated
   # thumbnails, set this to false. If disabled, regular thumbnails will be returned.
diff --git a/thumbnailing/i/mp4.go b/thumbnailing/i/mp4.go
new file mode 100644
index 00000000..82414378
--- /dev/null
+++ b/thumbnailing/i/mp4.go
@@ -0,0 +1,66 @@
+package i
+
+import (
+	"errors"
+	_ "image/jpeg"
+	"io/ioutil"
+	"os"
+	"os/exec"
+	"path"
+
+	"github.com/turt2live/matrix-media-repo/common/rcontext"
+	"github.com/turt2live/matrix-media-repo/thumbnailing/m"
+	"github.com/turt2live/matrix-media-repo/util"
+	"github.com/turt2live/matrix-media-repo/util/cleanup"
+)
+
+type mp4Generator struct {
+}
+
+func (d mp4Generator) supportedContentTypes() []string {
+	return []string{"video/mp4"}
+}
+
+func (d mp4Generator) supportsAnimation() bool {
+	return false
+}
+
+func (d mp4Generator) matches(img []byte, contentType string) bool {
+	return util.ArrayContains(d.supportedContentTypes(), contentType)
+}
+
+func (d mp4Generator) GenerateThumbnail(b []byte, contentType string, width int, height int, method string, animated bool, ctx rcontext.RequestContext) (*m.Thumbnail, error) {
+	key, err := util.GenerateRandomString(16)
+	if err != nil {
+		return nil, errors.New("mp4: error generating temp key: " + err.Error())
+	}
+
+	tempFile1 := path.Join(os.TempDir(), "media_repo."+key+".1.mp4")
+	tempFile2 := path.Join(os.TempDir(), "media_repo."+key+".2.png")
+
+	defer os.Remove(tempFile1)
+	defer os.Remove(tempFile2)
+
+	f, err := os.OpenFile(tempFile1, os.O_RDWR|os.O_CREATE, 0640)
+	if err != nil {
+		return nil, errors.New("mp4: error writing temp video file: " + err.Error())
+	}
+	_, _ = f.Write(b)
+	cleanup.DumpAndCloseStream(f)
+
+	err = exec.Command("ffmpeg", "-i", tempFile1, "-vf", "select=eq(n\\,0)", tempFile2).Run()
+	if err != nil {
+		return nil, errors.New("mp4: error converting video file: " + err.Error())
+	}
+
+	b, err = ioutil.ReadFile(tempFile2)
+	if err != nil {
+		return nil, errors.New("mp4: error reading temp png file: " + err.Error())
+	}
+
+	return pngGenerator{}.GenerateThumbnail(b, "image/png", width, height, method, false, ctx)
+}
+
+func init() {
+	generators = append(generators, mp4Generator{})
+}
-- 
GitLab