summaryrefslogtreecommitdiff
path: root/src/application/actions/thumb
diff options
context:
space:
mode:
Diffstat (limited to 'src/application/actions/thumb')
-rw-r--r--src/application/actions/thumb/get.php101
1 files changed, 101 insertions, 0 deletions
diff --git a/src/application/actions/thumb/get.php b/src/application/actions/thumb/get.php
new file mode 100644
index 0000000..c34bf92
--- /dev/null
+++ b/src/application/actions/thumb/get.php
@@ -0,0 +1,101 @@
+<?php
+
+use FFMpeg\Coordinate\TimeCode;
+use FFMpeg\FFMpeg;
+use FFMpeg\FFProbe;
+use mystic\forum\orm\Attachment;
+
+$attId = $_GET["attachment"] ?? throw new Exception("Missing attachment id");
+$attachment = new Attachment();
+$attachment->id = $attId;
+if (!$db->fetch($attachment)) {
+ http_response_code(404);
+ msg_error(__("No attachment exists with this id"));
+ exit;
+}
+
+$isImage = str_starts_with($attachment->mimeType, "image/");
+$isVideo = str_starts_with($attachment->mimeType, "video/");
+
+if (!$isImage && !$isVideo) {
+ http_response_code(400);
+ msg_error(__("Attachment is neither an image nor a video"));
+ exit;
+}
+
+$contentHash = hash("sha256", $attachment->contents);
+
+$cacheId = bin2hex($attachment->id);
+$cacheDir = sys_get_temp_dir() . "/mystic/forum/0/cache/thumbs/" . substr($cacheId, 0, 2) . "/" . substr($cacheId, 0, 8) . "/";
+if (!is_dir($cacheDir))
+ mkdir($cacheDir, recursive: true);
+
+$cacheFileData = $cacheDir . $cacheId . ".data";
+$cacheFileInfo = $cacheDir . $cacheId . ".info";
+
+if (is_file($cacheFileData) && is_file($cacheFileInfo)) {
+ $info = json_decode(file_get_contents($cacheFileInfo));
+ if ($info->contentHash === $contentHash) {
+ header("Content-Type: image/jpeg");
+ header("Cache-Control: max-age=86400");
+ //header("X-Debug-Content: $cacheFileData");
+ readfile($cacheFileData);
+ exit;
+ }
+}
+
+if ($isVideo) {
+ $suffix = (microtime(true) * 1000) . "-" . random_int(0, 99999);
+ $tempVid = sys_get_temp_dir() . "/video_" . $suffix;
+ file_put_contents($tempVid, $attachment->contents);
+ $tempImg = sys_get_temp_dir() . "/image_" . $suffix . ".jpg";
+
+ try {
+ $ffprobe = FFProbe::create();
+ /** @var string $duration */
+ $duration = $ffprobe
+ ->format($tempVid)
+ ->get("duration", "0");
+
+ $screenshotFramePoint = TimeCode::fromSeconds(floatval($duration) / 2.0);
+
+ $ffmpeg = FFMpeg::create();
+ $video = $ffmpeg->open($tempVid);
+ $screenshot = $video
+ ->frame($screenshotFramePoint)
+ ->save($tempImg);
+ $im = imagecreatefromjpeg($tempImg);
+ } finally {
+ if (is_file($tempVid)) unlink($tempVid);
+ if (is_file($tempImg)) unlink($tempImg);
+ }
+} elseif ($isImage) {
+ $im = imagecreatefromstring($attachment->contents);
+}
+
+$w = imagesx($im);
+$h = imagesy($im);
+$r = $w / floatval($h);
+
+if ($w > $h) {
+ $nw = THUMB_MAX_DIM;
+ $nh = floor($nw / $r);
+} else {
+ $nh = THUMB_MAX_DIM;
+ $nw = floor($r * $nh);
+}
+
+$thumb = imagecreatetruecolor($nw, $nh);
+imagecopyresampled($thumb, $im, 0, 0, 0, 0, $nw, $nh, $w, $h);
+imagedestroy($im);
+
+header("Content-Type: image/jpeg");
+header("Cache-Control: max-age=86400");
+imagejpeg($thumb, $cacheFileData, 40);
+imagedestroy($thumb);
+file_put_contents($cacheFileInfo, json_encode([
+ "format" => 1,
+ "contentHash" => $contentHash,
+ "created" => time(),
+], JSON_UNESCAPED_SLASHES));
+readfile($cacheFileData);