From c3314c440af055323f0d9c4a0908b07d081b6931 Mon Sep 17 00:00:00 2001
From: Reimar <mail@reim.ar>
Date: Sun, 11 Feb 2024 00:02:33 +0100
Subject: [PATCH] Add video thumbnails

---
 database.sqlite3        | Bin 20480 -> 20480 bytes
 index.js                |  23 +++++++++++++++--------
 migration.sql           |   1 -
 public/search/script.js |  15 +++++++++++++--
 public/style.css        |  29 +++++++++++++++++++++++++++++
 5 files changed, 57 insertions(+), 11 deletions(-)

diff --git a/database.sqlite3 b/database.sqlite3
index e2cbf910d2831eed3b4c12c0b3a5fcfe8baf7419..23c1a34490b283f5ad8522218ab5a56f77deec08 100644
GIT binary patch
delta 137
zcmZozz}T>WaY7cC9s_?GzX+cm-~G*k0=sy*8uggj#U&*fo6ILG@YQVI!Y3|hTAUkZ
z5?*3%65yQWn-*kYmTH+@XrX7}Qelu-X>8z_9FS#hZkARaULLNW6_!@)7?D{SUb#tu
nO@QSy1OMmEf)3C5`573v8CgU*h4U=S3^P)*HaYy^X92MRi<2k~

delta 180
zcmZozz}T>WaY7cC7z2MAzX+cg-~Ek^i+H&j-I&?MB_$b~(kEN+)vy#KmSk*Rz^5%}
z8klJ692sojnqE;BR^}g65t0#TP*j=`YGF|3nv++U<dx(V=@As-8{t{%Z<!P6>Y0*N
z8oo(^O@QST1OKVbf(i%t%}f|sMLC6IlY)ZtGE%ejGxBp&^@~z7a}$g7Q&P+Ha})DR
cN|RFc%Q90^^NaOC%JgyzOg1_E;b#GH0H&cjCjbBd

diff --git a/index.js b/index.js
index 066f03b..77f1cda 100644
--- a/index.js
+++ b/index.js
@@ -172,24 +172,31 @@ app.post("/api/upload_video", authorized(), fileUpload({ limits: { fileSize: 2 *
     const id = randomString(4);
     const tempPath = req.files.video.tempFilePath;
     const newPath = path.join(dirname(), "videos", `${id}.mp4`);
+    const thumbnailPath = path.join(dirname(), "videos", `${id}.png`);
 
     console.log(newPath);
 
-    const exitCode = await new Promise((resolve, _reject) => {
+    let exitCode = await new Promise(resolve => {
         const process = childProcess.spawn("ffmpeg", ["-i", tempPath, "-b:v", "1M", "-b:a", "192k", newPath]);
-        process.stderr.on("data", (data) => {
-            console.error(data.toString());
-        });
-        process.on("close", (code) => {
-            resolve(code);
-        })
+        process.stderr.on("data", (data) => console.error(data.toString()));
+        process.on("close", resolve);
     });
 
     if (exitCode !== 0) {
         throw new Error("ffmpeg failed");
     }
 
-    await dbRun("INSERT INTO videos (id, user_id, title, path) VALUES (?, ?, ?, ?)", id, userId, title, newPath);
+    exitCode = await new Promise(resolve => {
+        const process = childProcess.spawn("ffmpeg", ["-i", tempPath, "-ss", "00:00:01.000", "-vframes", "1", thumbnailPath]);
+        process.stderr.on("data", (data) => console.error(data.toString()));
+        process.on("close", resolve);
+    });
+
+    if (exitCode !== 0) {
+        throw new Error("thumbnail generation failed");
+    }
+
+    await dbRun("INSERT INTO videos (id, user_id, title) VALUES (?, ?, ?)", id, userId, title);
 
     return res.status(200).json({ ok: true });
 })
diff --git a/migration.sql b/migration.sql
index 569f0d9..dcff1ff 100644
--- a/migration.sql
+++ b/migration.sql
@@ -8,7 +8,6 @@ CREATE TABLE videos (
     id TEXT PRIMARY KEY NOT NULL,
     user_id INTEGER,
     title TEXT NOT NULL,
-    path TEXT NOT NULL,
     FOREIGN KEY(user_id) REFERENCES users(id)
 );
 
diff --git a/public/search/script.js b/public/search/script.js
index d11fb3f..4a3c1b4 100644
--- a/public/search/script.js
+++ b/public/search/script.js
@@ -24,9 +24,20 @@ function displayResponse(response) {
         + videos
             .map(v => {
                 console.log(v);
-                return `<li><p><a href="/watch?id=${v.id}">${v.title}</a> - ${v.author}</p></li>`
+                return `
+					<li>
+						<p class="video-item">
+							<img src="/videos/${v.id}.png" alt="">
+							<span class="video-info">
+								<a href="/watch?id=${v.id}">${v.title}</a>
+								<br>
+								by ${v.author}
+							</span>
+						</p>
+					</li>
+				`;
             })
-            .join("") 
+            .join("")
         + "</ul>";
 }
 
diff --git a/public/style.css b/public/style.css
index a7d5595..21465e1 100644
--- a/public/style.css
+++ b/public/style.css
@@ -62,12 +62,41 @@ body {
 ul#video-list {
     padding: 0;
     list-style: none;
+	width: 100%;
+	max-width: 800px;
+	margin: auto;
 }
 
 #video-player {
     max-height: 80vh;
 }
 
+.video-item {
+	display: flex;
+	flex-direction: row;
+	align-items: flex-start;
+	gap: 2em;
+	text-align: left;
+	background-color: rgba(255, 255, 255, 0.3);
+	box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+}
+
+.video-item img {
+	width: 200px;
+	height: 100px;
+	object-fit: contain;
+	background-color: black;
+}
+
+.video-item .video-info {
+	padding-top: 0.5em;
+}
+
+.video-item a {
+	font-size: 1.4em;
+	font-weight: bold;
+}
+
 header {
     display: flex;
     align-items: center;