summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorMadeline <46743919+MaddyUnderStars@users.noreply.github.com>2022-10-04 19:12:41 +1100
committerMadeline <46743919+MaddyUnderStars@users.noreply.github.com>2022-10-04 19:22:50 +1100
commit73923e269ae840c46199f8d15aa7d6c273c5ae71 (patch)
treeef332a3b9355fd27349e8de717af0adaa7b51fa5 /src
parentcapture exception in sentry for embed processing (diff)
downloadserver-73923e269ae840c46199f8d15aa7d6c273c5ae71.tar.xz
Video attachment support!
Diffstat (limited to 'src')
-rw-r--r--src/cdn/routes/attachments.ts44
-rw-r--r--src/cdn/util/FileStorage.ts2
2 files changed, 42 insertions, 4 deletions
diff --git a/src/cdn/routes/attachments.ts b/src/cdn/routes/attachments.ts
index 2a1b6f09..9bd256aa 100644
--- a/src/cdn/routes/attachments.ts
+++ b/src/cdn/routes/attachments.ts
@@ -5,6 +5,9 @@ import FileType from "file-type";
 import { HTTPError } from "lambert-server";
 import { multer } from "../util/multer";
 import imageSize from "image-size";
+import ffmpeg from "fluent-ffmpeg";
+import Path from "path";
+import { Duplex, Readable, Transform, Writable } from "stream";
 
 const router = Router();
 
@@ -15,6 +18,14 @@ const SANITIZED_CONTENT_TYPE = [
 	"application/xhtml+xml",
 ];
 
+const probe = (file: string): Promise<ffmpeg.FfprobeData> => new Promise((resolve, reject) => {
+	ffmpeg.setFfprobePath(process.env.FFPROBE_PATH as string);
+	ffmpeg.ffprobe(file, (err, data) => {
+		if (err) return reject(err);
+		return resolve(data);
+	});
+});
+
 router.post(
 	"/:channel_id",
 	multer.single("file"),
@@ -44,6 +55,13 @@ router.post(
 				height = dimensions.height;
 			}
 		}
+		else if (mimetype.includes("video") && process.env.FFPROBE_PATH) {
+			const root = process.env.STORAGE_LOCATION || "../";	// hmm, stolen from FileStorage
+			const out = await probe(Path.join(root, path));
+			const stream = out.streams[0];	// hmm
+			width = stream.width;
+			height = stream.height;
+		}
 
 		const file = {
 			id,
@@ -63,10 +81,10 @@ router.get(
 	"/:channel_id/:id/:filename",
 	async (req: Request, res: Response) => {
 		const { channel_id, id, filename } = req.params;
+		const { format } = req.query;
 
-		const file = await storage.get(
-			`attachments/${channel_id}/${id}/${filename}`,
-		);
+		const path = `attachments/${channel_id}/${id}/${filename}`;
+		let file = await storage.get(path);
 		if (!file) throw new HTTPError("File not found");
 		const type = await FileType.fromBuffer(file);
 		let content_type = type?.mime || "application/octet-stream";
@@ -75,6 +93,26 @@ router.get(
 			content_type = "application/octet-stream";
 		}
 
+		// lol, super gross
+		if (content_type.includes("video") && format == "jpeg" && process.env.FFMPEG_PATH) {
+			const promise = (): Promise<Buffer> => new Promise((resolve, reject) => {
+				ffmpeg.setFfmpegPath(process.env.FFMPEG_PATH as string);
+				const out: any[] = [];
+				const cmd = ffmpeg(Readable.from(file as Buffer))
+					.format("mjpeg")
+					.frames(1)
+					.on("end", () => resolve(Buffer.concat(out)))
+					.on("error", (err) => reject(err))
+				const stream = cmd.pipe();
+				stream.on("data", (data) => {
+					out.push(data)
+				});
+			});
+			const res = await promise();
+			file = res;
+			content_type = "jpeg";
+		}
+
 		res.set("Content-Type", content_type);
 		res.set("Cache-Control", "public, max-age=31536000");
 
diff --git a/src/cdn/util/FileStorage.ts b/src/cdn/util/FileStorage.ts
index 0e31a50e..9386663f 100644
--- a/src/cdn/util/FileStorage.ts
+++ b/src/cdn/util/FileStorage.ts
@@ -35,7 +35,7 @@ export class FileStorage implements Storage {
 
 	async set(path: string, value: any) {
 		path = getPath(path);
-		if (!fs.existsSync(dirname(path))) fs.mkdirSync(dirname(path));
+		if (!fs.existsSync(dirname(path))) fs.mkdirSync(dirname(path), { recursive: true });
 
 		value = Readable.from(value);
 		const cleaned_file = fs.createWriteStream(path);